IntVC6 16.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
;
; 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.
;

; Interrupt handling - BCM2711 / 2838
; Note that contrary to the Cortex-A72 TRM, the BCM2838 has opted to go with
; a GICv2 rather than the expected GICv3.

        EXPORT  InterruptVC6_Init

        EXPORT  VC6_HAL_IRQEnable
        EXPORT  VC6_HAL_IRQDisable
        EXPORT  VC6_HAL_IRQClear
        EXPORT  VC6_HAL_IRQSource
        EXPORT  VC6_HAL_IRQStatus
        EXPORT  VC6_HAL_FIQEnable
        EXPORT  VC6_HAL_FIQDisable
        EXPORT  VC6_HAL_FIQDisableAll
        EXPORT  VC6_HAL_FIQClear
        EXPORT  VC6_HAL_FIQSource
        EXPORT  VC6_HAL_FIQStatus
        EXPORT  VC6_HAL_IRQMax
        EXPORT  VC6_HAL_IRQProperties
        EXPORT  VC6_HAL_IRQSetCores
        EXPORT  VC6_HAL_IRQGetCores

        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:Proc
        GET     Hdr:HALEntries

        GET     hdr.BCM2835
        GET     hdr.StaticWS

        GBLL    GICDebug
GICDebug SETL   {FALSE} :LAND: HALDebug

     [ HALDebug
        IMPORT  HAL_DebugTX
        IMPORT  HAL_DebugHexTX4
        IMPORT  HAL_DebugTXStrInline
     ]

        AREA    |ARM$$code|, CODE, READONLY, PIC

; Mapping of OS device numbers to GIC interrupts, and vice-versa:
; - GIC SPI 0-31 are for QA7 interrupts (but with non-trivial mapping to iDev_QA7)
; - GIC SPI 32-63 map to iDev_ARM (first 8 only? rest are dupes of other GPU interrupts)
; - GIC SPI 64-127 map to iDev_GPU
; - GIC SPI 128-191 map to iDev_Ext
; - SPI literally means SPI, i.e. add an extra 32 to get the real GIC interrupt number
; This means that the bottom five bits of each IRQ can be a 1:1 mapping, and
; the top bits can be remapped via a table lookup.

OStoGIC
        ASSERT   iDev_GPU_Timer0 = 0
        DCB      3 ; iDev_GPU
        ASSERT   iDev_GPU_HostPort = 32
        DCB      4 ; iDev_GPU+32
        ASSERT   iDev_ARM_Timer = 64
        DCB      2 ; iDev_ARM
        ASSERT   iDev_Ext_Base = 96
        DCB      5 ; iDev_Ext
        ASSERT   iDev_Ext_HDMI_SEC = 128
        DCB      6 ; iDev_Ext+32
        ASSERT   iDev_GIC_Base = 160
        DCB      1 ; iDev_GIC
PPI_BASE * (. - OStoGIC)*32
        DCB      0 ; PPI core 0
        DCB      0 ; PPI core 1
        DCB      0 ; PPI core 2
        DCB      0 ; PPI core 3
MAX_IRQ * (. - OStoGIC)*32
        ALIGN

GICtoOS
        DCB      6 ; PPIs (also 7, 8, 9)
        DCB      5 ; iDev_GIC
        DCB      2 ; iDev_ARM
        DCB      0 ; iDev_GPU
        DCB      1 ; iDev_GPU+32
        DCB      3 ; iDev_Ext
        DCB      4 ; iDev_Ext+32
        ALIGN

        MACRO
        OStoGICNumber $irq, $temp
        ADR      $temp, OStoGIC
        LDRB     $temp, [$temp, $irq, LSR #5]
        BFI      $irq, $temp, #5, #8
        MEND

        MACRO
        OStoGICBitno $regno, $bitno, $irq
        ASSERT   $regno <> $irq
        ADR      $regno, OStoGIC
        LDRB     $regno, [$regno, $irq, LSR #5]
        AND      $bitno, $irq, #31
        MEND

        ; Map GIC IRQ number to OS GIC number
        ; Also returns CPU core number in $core, if it's a private interrupt
        MACRO
        GICtoOSNumber $irq, $core
        ADR      $core, GICtoOS
        CMP      $irq, #32
        LDRB     $core, [$core, $irq, LSR #5]
        BFI      $irq, $core, #5, #8
        ; Remap private interrupts
        MRCLO    p15, 0, $core, c0, c0, 5
        ANDLO    $core, $core, #3
        ADDLO    $irq, $irq, $core, LSL #5
        MEND

; Things the boot stubs do for us: 
; https://github.com/raspberrypi/tools/blob/master/armstubs/armstub7.S
;
; * Set GICD_CTLR to 3 for secondary cores (group 0 + 1 enabled). For the primary core it's not initialised.
; * Assign the first 192 interrupts to group 1 (i.e. private interrupts plus first 160 SPIs)
; * Set GICC_CTLR to &1e7: GIC bypasses disabled, AckCtl=1 (but irrelevant since it'll only apply to secure mode), group 0+1 enabled
; * Set GICC_PMR to &ff (= any priority level will pass)
; * Drops out of secure mode into non-secure SVC
;
; The upshot of the above is that we can only use the GIC for IRQs (not FIQs),
; and the extended GPU IRQs in the range >= iDev_Ext_Base+32 are inaccessible.
; However we do have control over the FIQ bypass, so by leaving that enabled we
; can access FIQs via the new BCM2838 FIQ controller.
;
; TODO - Do we still need BCM2835-style memory barriers?

MAX_FIQ * iDev_ARM_MiscGPU1

InterruptVC6_Init ROUT
        Entry   "v1-v3"
        LDR     a1, PeriBase
        ADD     a1, a1, #FIQ_Base
        STR     a1, FIQ_Base_Address
        LDR     a1, IntBase
        ADD     v1, a1, #GICD_Base
        STR     v1, GICD_Base_Address
        ADD     v2, a1, #GICC_Base
        STR     v2, GICC_Base_Address
        ; Disable distributor while we fiddle with it
        MOV     a1, #0
        STR     a1, [v1, #GICD_CTLR]
        ; How many IRQ lines does the GIC support?
        LDR     v3, [v1, #GICD_TYPER]
        AND     v3, v3, #&1F
        ADD     v3, v3, #1 ; num_interrupts/32

        ; Disable all interrupts
        MOV     a1, #-1
        ADD     a2, v1, #GICD_ICENABLER
        MOV     a3, v3
10
        STR     a1, [a2], #4
        SUBS    a3, a3, #1
        BNE     %BT10        
        ; Set priority & CPU target
        LDR     a1, =&80808080 ; Mid priority
        LDR     a2, =&01010101 ; CPU 0
        ADD     a3, v1, #GICD_IPRIORITYR
        MOV     a4, v3, LSL #3
20
        STR     a2, [a3, #GICD_ITARGETSR-GICD_IPRIORITYR]
        STR     a1, [a3], #4
        SUBS    a4, a4, #1
        BGT     %BT20
        ; Route QA7 mailbox interrupts to the expected cores (SMP module currently doesn't configure routing itself)
        MOV     a2, a2, LSL #1
        STR     a2, [v1, #GICD_ITARGETSR+36]
        MOV     a2, a2, LSL #1
        STR     a2, [v1, #GICD_ITARGETSR+40]
        MOV     a2, a2, LSL #1
        STR     a2, [v1, #GICD_ITARGETSR+44]
        ; Configure all interrupts as level-triggered
        MOV     a1, #0
        ADD     a2, v1, #GICD_ICFGR
        MOV     a3, v3, LSL #1
        SUB     a3, a3, #1
30
        STR     a1, [a2, #4]! ; Pre-increment (ICFGR0 is read-only)
        SUBS    a3, a3, #1
        BGT     %BT30
        ; We can now re-enable the controller
        MOV     a1, #1
        STR     a1, [v1, #GICD_CTLR]

        ; Configure CPU interface
        MOV     a1, #&FF
        STR     a1, [v2, #GICC_PMR] ; Highest PMR value = any priority interrupt will pass the test
        MOV     a1, #3
        STR     a1, [v2, #GICC_BPR] ; gggg.ssss binary point? not used ATM
        ; Enable CPU interface
        ; - IRQBypDisGrp1 is ignored since some of the secure mode settings take priority over this bit
        ; - FIQBypDisGrp1 is left at zero so that the bypass is enabled, allowing us to get FIQs from the BCM2838 FIQ controller
        MOV     a1, #1
        STR     a1, [v2, #GICC_CTLR]
        EXIT

;
; IRQ handling
;

VC6_HAL_IRQEnable ROUT
        CMP     a1, #MAX_IRQ
        BHS     ExitZero
      [ GICDebug
        Push    "lr"
        BL      HAL_DebugTXStrInline
        DCB     "eOG ",0
        ALIGN
        BL      HAL_DebugHexTX4
        Pull    "lr"
      ]
        LDR     a4, GICD_Base_Address
        OStoGICBitno a2, a3, a1
      [ GICDebug
        Push    "a1-a4,ip,lr"
        ORR     a1, a3, a2, LSL #5
        BL      HAL_DebugHexTX4
        MOV     a1, #10
        BL      HAL_DebugTX
        Pull    "a1-a4,ip,lr"
      ]
        MOV     ip, #1
        ADD     a4, a4, #GICD_ISENABLER
        MOV     a3, ip, LSL a3
        LDR     a1, [a4, a2, LSL #2] ; Get old state
        STR     a3, [a4, a2, LSL #2] ; Enable
        AND     a1, a1, a3
        MOV     pc, lr

VC6_HAL_IRQDisable ROUT
        CMP     a1, #MAX_IRQ
        BHS     ExitZero
        LDR     a4, GICD_Base_Address
        OStoGICBitno a2, a3, a1
        MOV     ip, #1
        ADD     a4, a4, #GICD_ICENABLER
        MOV     a3, ip, LSL a3
        LDR     ip, [a4, a2, LSL #2]! ; Get old state
        STR     a3, [a4] ; Set new state
        LDR     a4, [a4, #GICD_ISACTIVER-GICD_ICENABLER]
        TST     a4, a3 ; Was it active?
        BEQ     %FT90
        ORRS    a4, a2, a3, LSR #16 ; SGI?
        BFINE   a1, a2, #5, #8 ; Construct GIC interrupt number
        BNE     %FT80
        ; Look up the last SGI that was received for this core, it should correspond to the one we need to clear
        ; TODO - Potential interrupt hole regarding this?
        MRC     p15, 0, a2, c0, c0, 5
        ADR     a1, GIC_LastSGI
        AND     a2, a2, #3
        LDR     a1, [a1, a2, LSL #2]
80
        LDR     a2, GICC_Base_Address
        STR     a1, [a2, #GICC_EOIR]
90
        AND     a1, ip, a3
        DSB     SY
        MOV     pc, lr

VC6_HAL_IRQClear ROUT
        LDR     a4, GICC_Base_Address
        OStoGICNumber a1, ip
        CMP     a1, #16 ; SGI?
        BHS     %FT10
        ; Look up the last SGI that was received for this core, it should correspond to the one we need to clear
        MRC     p15, 0, ip, c0, c0, 5
        ADR     a1, GIC_LastSGI
        AND     ip, ip, #3
        LDR     a1, [a1, ip, LSL #2]
10
        STR     a1, [a4, #GICC_EOIR]
        DSB     SY        
        MOV     pc, lr

VC6_HAL_IRQSource ROUT
        LDR     a4, GICC_Base_Address
        LDR     a2, [a4, #GICC_IAR]
      [ GICDebug
        Push    "lr"
        BL      HAL_DebugTXStrInline
        DCB     "sGO ",0
        ALIGN
        MOV     a1, a2
        BL      HAL_DebugHexTX4
        Pull    "lr"
      ]
        UBFX    a1, a2, #0, #10
        CMP     a1, #&3FC ; Spurious interrupt?
        MOVHS   a1, #-1
        MOVHS   pc, lr
        ; Remap to OS
        GICtoOSNumber a1, ip
        ; If it's an SGI, make a note of the IAR value so HAL_IRQClear can clear it correctly
        TST     a2, #&3F0
        ADREQ   a4, GIC_LastSGI
        STREQ   a2, [a4, ip, LSL #2]
      [ GICDebug
        Push    "a1,lr"
        BL      HAL_DebugHexTX4
        MOV     a1, #10
        BL      HAL_DebugTX
        Pull    "a1,lr"
      ]
        MOV     pc, lr

VC6_HAL_IRQStatus ROUT
        CMP     a1, #MAX_IRQ
        BHS     ExitZero
        LDR     a4, GICD_Base_Address
        OStoGICBitno a2, a3, a1
        MOV     ip, #1
        ADD     a4, a4, #GICD_ISPENDR
        LDR     a1, [a4, a2, LSL #2]
        AND     a1, ip, a1, LSR a3
        MOV     pc, lr

;
; FIQ handling
;

; This implementation only provides access to the interrupts exposed by the
; BCM2828 FIQ controller, i.e. iDev_GPU and iDev_ARM. Technically we also have
; access to the interrupts from the QA7 controller (BCM2838 FIQ controller is
; the QA7 "GPU" FIQ?), but the QA7 interrupts aren't very exciting, so for now 
; just ignore them.

VC6_HAL_FIQEnable ROUT
        CMP     a1, #MAX_FIQ
        BHS     ExitZero
        DoMemBarrier ip
        LDR     ip, FIQ_Base_Address
        ADD     ip, ip, #FIQ_EN1
        MOV     a2, #1
        AND     a3, a1, #&1F         ; get bit in register
        MOV     a2, a2, LSL a3       ; bitmask
        MOV     a3, a1, LSR #5       ; shift to get relevant register
        LDR     a4, [ip, a3, LSL #2] ; get old enable mask
        STR     a2, [ip, a3, LSL #2] ; enable our bit
        AND     a1, a2, a4           ; test our bit in old mask
        DoMemBarrier ip
        MOV     pc, lr

VC6_HAL_FIQDisable ROUT
        CMP     a1, #MAX_FIQ
        BHS     ExitZero
        DoMemBarrier ip
        LDR     ip, FIQ_Base_Address
        ADD     ip, ip, #FIQ_DIS1
        MOV     a2, #1
        AND     a3, a1, #&1F         ; get bit in register
        MOV     a2, a2, LSL a3       ; bitmask
        MOV     a3, a1, LSR #5       ; shift to get relevant register
        LDR     a4, [ip, a3, LSL #2] ; get old enable mask
        STR     a2, [ip, a3, LSL #2] ; disable our bit
        AND     a1, a2, a4           ; test our bit in old mask
        DoMemBarrier ip
        MOV     pc, lr

VC6_HAL_FIQDisableAll ROUT
        MOV     a1, #-1
        LDR     ip, FIQ_Base_Address
        DoMemBarrier a2
        STR     a1, [ip, #FIQ_DIS1]
        STR     a1, [ip, #FIQ_DIS2]
        STR     a1, [ip, #FIQ_DISB]
        DoMemBarrier a2
        MOV     pc, lr

VC6_HAL_FIQClear ROUT
        MOV     pc, lr

VC6_HAL_FIQSource ROUT
        DoMemBarrier ip
        LDR     a2, FIQ_Base_Address
        LDRB    a1, [a2, #FIQ_PENDB]  ; note, LDRB so we ignore bits 8-31
        CLZ     a1, a1
        RSBS    a1, a1, #31
        ADDPL   a1, a1, #iDev_ARM_Timer ; 64
        BPL     %FT90
        LDR     a1, [a2, #FIQ_PEND2]
        CLZ     a1, a1
        RSBS    a1, a1, #31
        ADDPL   a1, a1, #iDev_GPU_HostPort ; 32
        BPL     %FT90
        LDR     a1, [a2, #FIQ_PEND1]
        CLZ     a1, a1
        RSB     a1, a1, #31
90
        DoMemBarrier ip
        MOV     pc, lr

VC6_HAL_FIQStatus ROUT
        CMP     a1, #MAX_FIQ
        BHS     ExitZero
        DoMemBarrier ip
        LDR     ip, FIQ_Base_Address
        ASSERT  FIQ_PEND1 = 0
        MOV     a2, #1
        AND     a3, a1, #&1F         ; get bit in register
        MOV     a2, a2, LSL a3       ; bitmask
        MOV     a3, a1, LSR #5       ; shift to get relevant register
        LDR     a4, [ip, a3, LSL #2] ; get pending mask
        AND     a1, a2, a4           ; test our bit
        DoMemBarrier ip
        MOV     pc, lr

;
; Misc
;

VC6_HAL_IRQMax ROUT
        MOV     a1, #MAX_IRQ
        MOV     pc, lr

; In: a1 = device number
; Out: a1 = IRQ mask
;      a2 = FIQ mask
;           bits 0-15 of each register give cores that the interrupt can be
;           assigned to
;           bit 30 = private flag
;           bit 31 = interrupt can be routed to multiple cores at once
VC6_HAL_IRQProperties ROUT
        CMP     a1, #MAX_IRQ
        MOV     a2, #0
        MOVHS   a1, #0
        MOVHS   pc, lr
        SUBS    a1, a1, #PPI_BASE
        BGE     %FT90
        ; SPI
        CMP     a1, #MAX_FIQ-PPI_BASE
        MOVLT   a2, #1 ; Fixed FIQ routing to core 0
        MOV     a1, #&F + HALIRQProperty_Multicore
        MOV     pc, lr
90
        ; PPI
        MOV     a1, a1, LSR #5
        MOV     a3, #1
        MOV     a1, a3, LSL a1
        ORR     a1, a1, #HALIRQProperty_Private
        MOV     pc, lr

; In: a1 = device number
;     a2 = desired core mask
; Out: a1 = actual core mask
VC6_HAL_IRQSetCores ROUT
        CMP     a1, #MAX_IRQ
        BHS     ExitZero
        SUBS    a2, a1, #PPI_BASE
        BGE     %FT80
        ; SPI
        LDR     a4, GICD_Base_Address
        OStoGICNumber a1, ip
        ADD     a4, a4, a1
        AND     a1, a2, #&F
        STRB    a1, [a4, #GICD_ITARGETSR]
        MOV     pc, lr

VC6_HAL_IRQGetCores ; read-only version of IRQSetCores
        CMP     a1, #MAX_IRQ
        BHS     ExitZero
        SUBS    a2, a1, #PPI_BASE
        BGE     %FT80
        ; SPI
        LDR     a4, GICD_Base_Address
        OStoGICNumber a1, ip
        ADD     a4, a4, #GICD_ITARGETSR
        LDRB    a1, [a4, a1]
        MOV     pc, lr
80
        ; PPI
        MOV     a2, a2, LSR #5
        MOV     a1, #1
        MOV     a1, a1, LSL a2
        MOV     pc, lr

ExitZero
        MOV     a1, #0
        MOV     pc, lr

        END