; ; Copyright (c) 2019, RISC OS Open Ltd ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions are met: ; * Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; * Redistributions in binary form must reproduce the above copyright ; notice, this list of conditions and the following disclaimer in the ; documentation and/or other materials provided with the distribution. ; * Neither the name of RISC OS Open Ltd nor the names of its contributors ; may be used to endorse or promote products derived from this software ; without specific prior written permission. ; ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ; POSSIBILITY OF SUCH DAMAGE. ; GET Hdr:ListOpts GET Hdr:CPU.Arch GET Hdr:Macros GET Hdr:Proc GET Hdr:System GET Hdr:PCI GET hdr.BCM2835 GET hdr.StaticWS IMPORT HAL_CounterDelay IMPORT memcpy EXPORT PCI_Init EXPORT HAL_PCIReadConfigByte EXPORT HAL_PCIReadConfigHalfword EXPORT HAL_PCIReadConfigWord EXPORT HAL_PCIWriteConfigByte EXPORT HAL_PCIWriteConfigHalfword EXPORT HAL_PCIWriteConfigWord EXPORT HAL_PCISlotTable EXPORT HAL_PCIAddresses AREA |Asm$$Code|, CODE, READONLY, PIC ; Timeouts PCILinkUpTries * 1000 PCILinkUpDelay * 1000 ; Microseconds PCIResetDelay * 1000 ; Microseconds ; Config layout PCIViaMemBase * &00100000 ; Non-zero, just so nobody thinks it's disabled PCIWinMemBaseHi * 0 PCIWinMemBaseLo * &00000000 ; PCI view offset CPUWinMemBaseHi * 6 CPUWinMemBaseLo * &00000000 ; CPU view offset CPUWinMemSize * &08000000 ; Arbitrary, but at least big enough to encompass &00000000...PCIViaMemBase PCI_Init ROUT CPUDetect a1 MOVLS pc, lr Push "v1-v3,lr" ; The PCIe registers have already been mapped in as part of PeriBase2 LDR v1, PeriBase2 ADD v1, v1, #PCIe_Base ; First get into a known state in case of a soft reset LDR v2, =PCIe_RGR1_SW_INIT1 LDR a1, [v1, v2] ORR a1, a1, #PCIe_RGR1_SW_INIT1_POWERDOWN :OR: PCIe_RGR1_SW_INIT1_RESET STR a1, [v1, v2] MOV a1, #PCIResetDelay BL HAL_CounterDelay ; Power up the controller LDR a4, [v1, v2] BIC a4, a4, #PCIe_RGR1_SW_INIT1_POWERDOWN STR a4, [v1, v2] ; Knock out the unused controller interrupts MVN a1, #0 LDR a3, =PCIe_INTR2_CLEAR STR a1, [v1, a3] LDR lr, [v1, a3] ; Flush LDR a3, =PCIe_INTR2_MASK_SET STR a1, [v1, a3] LDR lr, [v1, a3] ; Flush ; Bring out of reset BIC a4, a4, #PCIe_RGR1_SW_INIT1_RESET STR a4, [v1, v2] ; Wait up to 1s for link up, we're pretty sure the VIA USB chip is there LDR v2, =PCIe_LINK_STATUS MOV v3, #PCILinkUpTries 10 LDR a1, [v1, v2] AND a1, a1, #PCIe_LINK_STATUS_PORT :OR: PCIe_LINK_STATUS_DOWNLINK_ACT :OR: PCIe_LINK_STATUS_PHYLINK_UP TEQ a1, #PCIe_LINK_STATUS_PORT :OR: PCIe_LINK_STATUS_DOWNLINK_ACT :OR: PCIe_LINK_STATUS_PHYLINK_UP BEQ %FT20 MOV a1, #PCILinkUpDelay BL HAL_CounterDelay SUBS v3, v3, #1 BNE %BT10 ; Oh crap, timed out, don't admit the PCI bus is present Pull "v1-v3, pc" 20 ; Sanity check the built in bridge LDRH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf_DeviceID] MOVW a2, #&2711 ; BCM2711 TEQ a1, a2 LDREQH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf_VendorID] MOVWEQ a2, #&14E4 ; Broadcom TEQEQ a1, a2 LDREQB a1, [v1, #PCIConf_HeaderType] TEQEQ a1, #1 ; Bridge Pull "v1-v3, pc",NE ; Ensure the VIA USB chip appears as bus 1/devfn 0 MOV a1, #0 STRB a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_PrimaryBus] MOV a1, #1 STRB a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_SecondaryBus] STRB a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_SubordinateBus] ; Pass memory transactions to the secondary MOV a1, #PCICmd_BusMaster :OR: PCICmd_Memory STRB a1, [v1, #PCIe_BRIDGE_CFG + PCIConf_Command] ; Set limit < base, no prefetchable memory MOV a1, #&0010 STRH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_PrefetchableBase] MOV a1, #&0000 STRH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_PrefetchableLimit] ; Set limit & base for memory transactions MOV a1, #PCIWinMemBaseLo:SHR:16 STRH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_MemoryBase] LDR a2, =CPUWinMemSize - 1 ; Inclusive limit ADD a1, a2, #PCIWinMemBaseLo MOV a1, a2, LSR #16 STRH a1, [v1, #PCIe_BRIDGE_CFG + PCIConf1_MemoryLimit] ; Unlike platforms where there are sockets on the motherboard into which arbitrary ; PCI cards could be plugged, there are only known devices present, so there's no need ; to go probing and allocating memory space - just set up the BARs from hardcoded knowledge. MOV a4, #PCIViaMemBase MOV a3, #PCIConf0_BaseAddresses MOV a2, #0 MOV a1, #0 ; VL805 is dev/bus/fn 0 BL HAL_PCIWriteConfigWord MOV a4, #PCICmd_BusMaster :OR: PCICmd_Memory MOV a3, #PCIConf_Command MOV a2, #0 MOV a1, #0 ; VL805 is dev/bus/fn 0 BL HAL_PCIWriteConfigByte ; PCI side of outbound window MOV a1, #PCIWinMemBaseLo LDR a3, =PCIe_PCI_MEM_LO_WIN0 STR a1, [a3, v1]! MOV a1, #PCIWinMemBaseHi STR a1, [a3, #PCIe_PCI_MEM_HI_WIN0 - PCIe_PCI_MEM_LO_WIN0] ; CPU side of outbound window MOV a1, #CPUWinMemBaseLo MOV a2, #CPUWinMemBaseHi STR a2, [a3, #PCIe_CPU_MEM_BASE_HI_WIN0 - PCIe_PCI_MEM_LO_WIN0] LDR a4, =CPUWinMemSize - 1 ; Inclusive limit ADDS a1, a1, a4 ADC a2, a2, #0 STR a2, [a3, #PCIe_CPU_MEM_LIMIT_HI_WIN0 - PCIe_PCI_MEM_LO_WIN0] MOV a4, #CPUWinMemBaseLo:SHR:20 ; Now in MB ASSERT PCIe_CPU_MEM_LIMIT_SHIFT = 20 BFC a1, #0, #20 ; Now in MB and promoted ORR a1, a1, a4, LSL #PCIe_CPU_MEM_BASE_SHIFT STR a1, [a3, #PCIe_CPU_MEM_BASE_LIMIT_WIN0 - PCIe_PCI_MEM_LO_WIN0] Pull "v1-v3, pc" HAL_PCIWriteConfigByte ROUT MOV ip, #-1 B %FT10 HAL_PCIWriteConfigHalfword MOV ip, #-2 B %FT10 HAL_PCIWriteConfigWord MOV ip, #-4 B %FT10 HAL_PCIReadConfigByte MOV ip, #1 B %FT10 HAL_PCIReadConfigHalfword MOV ip, #2 B %FT10 HAL_PCIReadConfigWord MOV ip, #4 10 CMP a1, #1 ; Range check the bus CMPCC a2, #256 ; Range check the 5:3 devfn MVNCS a1, #0 [ {FALSE} MOVCS pc, lr | ; Accessing non existant bus/devfn combinations stalls for a few seconds ORRS a1, a1, a2 MVNNE a1, #0 MOVNE ip, ip, LSL #3 RSBNE ip, ip, #32 MOVNE a1, a1, LSR ip ; Clip to byte/halfword/word MOVNE pc, lr ; So map bus 0/devfn 0 in RISC OS to bus 1/devfn 0 via the bridge MOV a1, #1 ] ; Massage bus/devfn into index format MOV a1, a1, LSL #PCIe_EXT_CFG_INDEX_BUS_SHIFT ASSERT (PCIe_EXT_CFG_INDEX_DEV_SHIFT - PCIe_EXT_CFG_INDEX_FN_SHIFT) = 3 ASSERT (PCIe_EXT_CFG_INDEX_BUS_SHIFT - PCIe_EXT_CFG_INDEX_DEV_SHIFT) = 5 ASSERT PCIe_EXT_CFG_INDEX_DEV_SHIFT > PCIe_EXT_CFG_INDEX_FN_SHIFT ORR a1, a1, a2, LSL #PCIe_EXT_CFG_INDEX_FN_SHIFT LDR a2, PeriBase2 ADD a2, a2, #PCIe_Base ADD a2, a2, #PCIe_EXT_CFG_INDEX STR a1, [a2] SUB a2, a2, #PCIe_EXT_CFG_INDEX - PCIe_EXT_CFG_DATA MOVS ip, ip BPL %FT20 ; Write op CMP ip, #-2 STRGTB a4, [a2, a3] STREQH a4, [a2, a3] STRLT a4, [a2, a3] MOV pc, lr 20 ; Read op CMP ip, #2 LDRLTB a1, [a2, a3] LDREQH a1, [a2, a3] LDRGT a1, [a2, a3] MOV pc, lr HAL_PCIAddresses ROUT Push "v1, lr" CMP a2, #7*4 MOVCS a3, #7*4 MOVCC a3, a2 MOV v1, a3 ; The smaller of the two ADR a2, %FT10 BL memcpy MOV a1, v1 Pull "v1, pc" 10 DCD 0 ; Memory space description DCD 0 DCD &1000 DCD 0 ; IO space description DCD 0 DCD &1000 DCD 0 ; Inbound RAM adjustment HAL_PCISlotTable ROUT CMP a2, #4 + 8 ADRCS ip, %FT30 ASSERT (%FT40 - %FT30) = 4 + 8 LDMCSIA ip, {a2-a4} STMCSIA a1, {a2-a4} MOV a1, #4 + 8 MOV pc, lr 30 DCD 1 ; Table comprising only 1 slot DCB 0 ; Bus DCB 0*8 ; Devno DCB 255, 255, 255, 255 ; Interrupt numbers (none currently) DCB 2_1111 ; Flags DCB 0 ; Slot number for motherboard ALIGN 40 END