; 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.omap3530
        GET     hdr.StaticWS
        GET     hdr.Timers
        GET     hdr.PRCM
        GET     hdr.USB
        GET     hdr.GPIO

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

        EXPORT  USB_Init

        EXPORT  HAL_USBControllerInfo
        EXPORT  HAL_USBPortPower
        EXPORT  HAL_USBPortStatus
        EXPORT  HAL_USBPortIRQ

        IMPORT  HAL_CounterDelay
        IMPORT  DebugHALPrintReg
        IMPORT  GPIOx_SetAsOutput
        IMPORT  GPIOx_SetOutput

; USB PHY power is controlled via GPIO
;USB2_PHY_GPIO * 147  -> board config
USB2_PHY_Reset_delay * 10000 ; 10msec

OHCI_IRQ  * 76
EHCI_IRQ  * 77
MUSB_IRQ  * 92

USB_Init
        Push    "lr"
        ; Initialise USB
        ; This assumes we're running on a rev C beagleboard, i.e. we're using EHCI via an external PHY
        ; This provides us with two ports, although the board only has a transceiver & connector for one (port #2)
        ; This code is roughly the same as omap_start_ehc() in the linux sources, except DPLL5 will already be running (initialised in PRCM_SetClocks)
        LDR     a1, L4_ClockMan_Log
        ; Set up autoidle/sleep mode stuff for USBHOST
        MOV     a2, #0
        LDR     a3, =CM_AUTOIDLE_USBHOST
        STR     a2, [a1, a3]
        LDR     a3, =CM_SLEEPDEP_USBHOST
        STR     a2, [a1, a3]
        LDR     a3, =CM_CLKSTCTRL_USBHOST
        STR     a2, [a1, a3]
        ; Enable the clocks
        MOV     a2, #1
        LDR     a3, =CM_ICLKEN_USBHOST
        STR     a2, [a1, a3]
        MOV     a2, #3
        LDR     a3, =CM_FCLKEN_USBHOST
        STR     a2, [a1, a3]
        ; Bring the external PHY out of reset
        LDRB    a1, [sb, #BoardConfig_USBGPIO]
        CMP     a1, #255
        BEQ     %FT10 ; Do nothing if no GPIO?
        MOV     a2, #0 ; Set pin to 0 to place PHY in reset
        BL      GPIOx_SetAsOutput
        ; Wait a while
        LDR     a1, =USB2_PHY_Reset_delay
        BL      HAL_CounterDelay
        ; Disable the integrated STP pull-up resistor
        LDR     a1, L4_USB_Host_Log
        ADD     a2, a1, #EHCI_BASE
        LDR     a3, =&81870090 ; Write 0x90 to port 1 addr 7 (surely this should be port 2?)
        STR     a3, [a2, #EHCI_INSNREG05_ULPI]
        ; Wait
5
        LDR     a3, [a2, #EHCI_INSNREG05_ULPI]
        TST     a3, #&80000000
        BEQ     %BT5
        ; Force PHY to HS
        LDR     a3, =&81840040 ; Write 0x40 to port 1 addr 4
        STR     a3, [a2, #EHCI_INSNREG05_ULPI]
        ; Wait
5
        LDR     a3, [a2, #EHCI_INSNREG05_ULPI]
        TST     a3, #&80000000
        BEQ     %BT5
        ; Enable USBHOST TLL clocks
        LDR     a1, L4_ClockMan_Log
        LDR     a2, =CM_FCLKEN3_CORE
        LDR     a3, [a1, a2]
        ORR     a3, a3, #1:SHL:2 ; EN_USBTLL
        STR     a3, [a1, a2]
        LDR     a2, =CM_ICLKEN3_CORE
        LDR     a3, [a1, a2]
        ORR     a3, a3, #1:SHL:2 ; EN_USBTLL
        STR     a3, [a1, a2]
        ; Disable auto-idle
        LDR     a2, =CM_AUTOIDLE3_CORE
        LDR     a3, [a1, a2]
        BIC     a3, a3, #1:SHL:2
        STR     a3, [a1, a2]
        ; Wait for TLL to become active
5
        LDR     a2, =CM_IDLEST3_CORE
        LDR     a3, [a1, a2]
        TST     a3, #1:SHL:2
        BNE     %BT5
        ; TLL soft reset
        LDR     a1, L4_USBTLL_Log
        MOV     a2, #2
        STR     a2, [a1, #USBTLL_SYSCONFIG]
5
        LDR     a2, [a1, #USBTLL_SYSSTATUS]
        TST     a2, #1
        BEQ     %BT5
        ; Disable auto-idle (TODO - why?), enable wakeup
        MOV     a2, #&10C
        STR     a2, [a1, #USBTLL_SYSCONFIG]
        ; Now configure UHH
        ; no auto-idle/standby (TODO - why?)
        LDR     a1, L4_USB_Host_Log
        LDR     a2, =&110C
        STR     a2, [a1, #UHH_SYSCONFIG]
        ; Configure for UPLI PHY mode
        MOV     a2, #&1C ; ULPI bypass enabled, INCR4/8/16 bursts enabled
        STR     a2, [a1, #UHH_HOSTCONFIG]
5
        LDR     a2, [a1, #UHH_HOSTCONFIG]
        TST     a2, #1
        BNE     %BT5
        ; Wait for things to settle
        LDR     a1, =USB2_PHY_Reset_delay
        BL      HAL_CounterDelay
        ; Now wake up the PHY
        LDRB    a1, [sb, #BoardConfig_USBGPIO]
        MOV     a2, #1
        BL      GPIOx_SetOutput
        ; Success!
10
        Pull    "pc"

        ; a1 = interface #
        ; a2 = usbinfo ptr
        ; a3 = sizeof(usbinfo)
        ; Return sizeof(usbinfo) or 0 for fail
HAL_USBControllerInfo
        CMP     a3, #USBINFO_SIZEOF
        MOVNE   a1, #0
        MOVNE   pc, lr
        CMP     a1, #1
        MOVHI   a1, #0
        MOVHI   pc, lr
        BEQ     %FT10
        ; Fill in the usbinfo struct
        MOV     a1, #1 ; EHCI
        STR     a1, [a2, #USBINFO_TYPE]
        MOV     a1, #USBINFO_FLAG_32bit_Regs ; EHCI did seem to work OK without forcing 32bit register access, but it is technically needed so I'll leave it set.
        STR     a1, [a2, #USBINFO_FLAGS]
        LDR     a1, L4_USB_Host_Log
        ADD     a1, a1, #EHCI_BASE
        STR     a1, [a2, #USBINFO_HW]
        MOV     a1, #EHCI_IRQ
        STR     a1, [a2, #USBINFO_DEVNO]
        MOV     a1, #USBINFO_SIZEOF
        MOV     pc, lr
10
        MOV     a1, #2 ; MUSBMHDRC
        STR     a1, [a2, #USBINFO_TYPE]
        MOV     a1, #0
        STR     a1, [a2, #USBINFO_FLAGS]
        LDR     a1, L4_USB_OTG_Log
        STR     a1, [a2, #USBINFO_HW]
        MOV     a1, #MUSB_IRQ
        STR     a1, [a2, #USBINFO_DEVNO]
        MOV     a1, #USBINFO_SIZEOF
        MOV     pc, lr


        ; These are unused for EHCI/MUSB
HAL_USBPortPower
HAL_USBPortStatus
HAL_USBPortIRQ
        MOV     a1, #-1
        MOV     pc, lr

        END