; Copyright 1996 Acorn Computers 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.
;
; > Copro15ops

;macros for Coprocessor #15 operations (configuration), which run-time detect
;and cater for ARM 6,7,8,A (A=StrongARM).
;Routines detect which ARM directly by reading ARM ID register (avoids memory reads).

; 24-01-96 MJS Created
; 07-10-96 MJS Updated for proper ARM 810 support (not needed for RO 3.70)
; 10-03-97 MJS A few additions for chocolate flavour screen handling (possible
;              Domain and FSR register use) in Phoebe OS

ARM_config_cp        CP 15  ;coprocessor number for configuration control

ARM_ID_reg           CN  0  ;processor ID
ARM_control_reg      CN  1  ;control
ARM_tbase_reg        CN  2  ;translation base (MMU)
ARM_domain_reg       CN  3  ;domain access control (MMU)
ARM_FSR_reg          CN  5  ;Fault status reg (MMU, read only on ARM 6/7)
ARM_FAR_reg          CN  6  ;Fault address reg (MMU, read only on ARM 6/7)

ARM67_TLBflush_reg   CN  5  ;TLB flush, ARMs 6 or 7
ARM67_TLBpurge_reg   CN  6  ;TLB purge entry, ARMs 6 or 7
ARM67_cacheflush_reg CN  7  ;cache flush, ARMs 6 or 7

ARM8A_cache_reg      CN  7  ;cache operations, ARMs 8 or StrongARM
ARM8A_TLB_reg        CN  8  ;TLB operations, ARMs 8 or StrongARM

ARM8_cacheLD_reg     CN  9  ;cache lock-down, ARM 8
ARM8_TLBLD_reg       CN 10  ;TLB lock-down, ARM 8

ARM8_CTC_reg         CN 15  ;Clock and test configuration

ARMA_TCI_reg         CN 15  ;Test,Clock and Idle control

;so that AASM will accept the general value for MCR CRm field
C0  CN  0
C1  CN  1
C2  CN  2
C3  CN  3
C4  CN  4
C5  CN  5
C6  CN  6
C7  CN  7
C8  CN  8
C9  CN  9
C10 CN 10
C11 CN 11
C12 CN 12
C13 CN 13
C14 CN 14
C15 CN 15


;
; ----------------- all ARMs ---------------------------------------------
;

;set MMU translation base
        MACRO
        ARM_MMU_transbase $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM_tbase_reg,C0,0
        MEND

;set MMU domain access register
        MACRO
        ARM_MMU_domain $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM_domain_reg,C0,0
        MEND

;read control register
        MACRO
        ARM_read_control $reg,$cond
        MRC$cond ARM_config_cp,0,$reg,ARM_control_reg,C0,0
        MEND

;set control register
        MACRO
        ARM_write_control $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM_control_reg,C0,0
        MEND

;read MMU fault status
        MACRO
        ARM_read_FSR $reg,$cond
        MRC$cond ARM_config_cp,0,$reg,ARM_FSR_reg,C0,0
        MEND

;read MMU fault address
        MACRO
        ARM_read_FAR $reg,$cond
        MRC$cond ARM_config_cp,0,$reg,ARM_FAR_reg,C0,0
        MEND

;read ID register to register $id
;bits 15:12 of returned ID will be 0,7,8,10 for ARM 6,7,8,A
        MACRO
        ARM_read_ID $id,$cond
        MRC$cond ARM_config_cp,0,$id,ARM_ID_reg,C0,0
        MEND

;read ARM 'number' (6,7,8,&A currently) into $num
        MACRO
        ARM_number $num
        ARM_read_ID $num
        ANDS     $num,$num,#&F000
        MOVEQ    $num,#&6000       ;catch and correct daft ARM 6 ID layout
        MOV      $num,$num,LSR #12
        MEND

;flush whole TLB (both data and instruction for StrongARM)
;trashes $temp
        MACRO
        ARM_flush_TLB $temp
        ARM_read_ID $temp
        AND      $temp,$temp,#&F000
        CMP      $temp,#&8000   ;ARM 8?
        CMPNE    $temp,#&A000   ;or StrongARM?
        MCRNE    ARM_config_cp,0,R0,ARM67_TLBflush_reg,C0,0
        MCREQ    ARM_config_cp,0,R0,ARM8A_TLB_reg,C7,0
        MEND

;flush whole cache (both data and instruction for StrongARM),
;without worrying about any cache cleaning
;trashes $temp
        MACRO
        ARM_flush_cache $temp
        ARM_read_ID $temp
        AND      $temp,$temp,#&F000
        CMP      $temp,#&8000   ;ARM 8?
        CMPNE    $temp,#&A000   ;or StrongARM?
        MCRNE    ARM_config_cp,0,R0,ARM67_cacheflush_reg,C0,0
        MCREQ    ARM_config_cp,0,R0,ARM8A_cache_reg,C7,0
        MEND

;flush whole TLB and cache (both data and instruction for StrongARM),
;without worrying about any cache cleaning
;trashes $temp
        MACRO
        ARM_flush_cacheandTLB $temp
        ARM_read_ID $temp
        AND      $temp,$temp,#&F000
        CMP      $temp,#&8000   ;ARM 8?
        CMPNE    $temp,#&A000   ;or StrongARM?
        MCRNE    ARM_config_cp,0,R0,ARM67_cacheflush_reg,C0,0
        MCRNE    ARM_config_cp,0,R0,ARM67_TLBflush_reg,C0,0
        MCREQ    ARM_config_cp,0,R0,ARM8A_cache_reg,C7,0
        MCREQ    ARM_config_cp,0,R0,ARM8A_TLB_reg,C7,0
        MEND

;
; -------------- ARM 6,7 only --------------------------------------------
;

;flush cache
        MACRO
        ARM67_flush_cache $cond
        MCR$cond ARM_config_cp,0,R0,ARM67_cacheflush_reg,C0,0
        MEND

;flush TLB
        MACRO
        ARM67_flush_TLB $cond
        MCR$cond ARM_config_cp,0,R0,ARM67_TLBflush_reg,C0,0
        MEND

;flush TLB entry, virtual address in $reg
        MACRO
        ARM67_flush_TLBentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM67_TLBpurge_reg,C0,0
        MEND

;
; -------------- ARM 810 only ----------------------------------------------
;

 [ ARM810support

;turn off branch prediction
; - the forced mispredicted branch ensures that the predictor is trapped in
;   this code segment when turned off
; - corrupts $temp and status flags
;
        MACRO
        ARM8_branchpredict_off $temp
01
        ARM_read_control $temp
        BIC $temp,$temp,#&800        ;z bit (branch prediction)
        ARM_write_control $temp
        SEC                          ;set carry flag
        BCC %BT01
        MEND

;turn on branch prediction
        MACRO
        ARM8_branchpredict_on $temp
        ARM_read_control $temp
        ORR $temp,$temp,#&800        ;z bit (branch prediction)
        ARM_write_control $temp
        MEND

;flush branch prediction, which is sufficient for an IMB (instruction memory
;barrier) on ARM 810, BUT...
; - intended for in line use only, where efficiency matters, or SWI call is
;   awkward
; - general code should use SWI OS_SynchroniseCodeAreas to implement
;   an IMB (instruction memory barrier) in future proof, ARM independent way
; - kernel code may use this without regard to which ARM running - ie. assumed
;   harmless on other ARMs
;
        MACRO
        ARM8_branchpredict_flush
        SUB PC,PC,#4        ;flush, because PC is written by data op
        MEND

;clean cache entry
; - segment,index spec in $reg
; - bits 4..6   = segment (0..7)
; - bits 26..31 = index   (0..63)
; - all other bits zero
        MACRO
        ARM8_clean_IDCentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C11,1
        MEND

;flush cache entry -  segment,index spec in $reg, as for ARM8_clean_IDCentry
        MACRO
        ARM8_flush_IDCentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C7,1
        MEND

;clean and flush cache entry -  segment,index spec in $reg, as for ARM8_clean_IDCentry
;
;if ARM810cleanflushbroken is TRUE, interrupts *must* be currently diabled (see below)
;
        MACRO
        ARM8_cleanflush_IDCentry $reg,$cond
  [ ARM810cleanflushbroken
        ARM8_clean_IDCentry $reg,$cond
        ARM8_flush_IDCentry $reg,$cond
  |
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C15,1
  ]
        MEND

;fully clean and flush cache (assumes no locked-down entries to preserve)
;
;if ARM810cleanflushbroken is TRUE, then we have to make sure interrupts are disabled during
;the sequence of 2 MCRs that make up ARM8_cleanflush_IDCentry, to avoid an interrupt hole.
;The hole occurs if an interrupt fills and dirties the particular cache entry after the clean
;but before the flush. We don't have this problem with StrongARM, because the entry is
;specified by virtual address, and RISC OS only cleans/flushes address space not currently
;involved in interrupts.
;
  [ ARM810cleanflushbroken

        MACRO
        ARM8_cleanflush_IDC $temp,$temp2
        ;for simplicity, disable interrupts during entire operation - 26-bit assumed
        MOV  $temp2,pc
        AND  $temp2,$temp2,#I_bit
        EOR  $temp2,$temp2,#I_bit                ;temp := <current I> EOR <I set>
        TEQP $temp2,pc                           ;disable I
        MOV  $temp,#0                            ;initial segment and index
01
        ARM8_cleanflush_IDCentry $temp
        ADD $temp,$temp,#1 :SHL: 26              ;next index
        CMP $temp,#1 :SHL: 26                    ;last index done if index field wrapped to 0
        BHS %BT01
        ADD $temp,$temp,#1 :SHL: 4               ;next segment
        CMP $temp,#8 :SHL: 4                     ;8 segments done?
        BLO %BT01
        TEQP $temp2,pc                           ;restore I
        MEND

  |

        MACRO
        ARM8_cleanflush_IDC $temp
        MOV $temp,#0                             ;initial segment and index
01
        ARM8_cleanflush_IDCentry $temp
        ADD $temp,$temp,#1 :SHL: 26              ;next index
        CMP $temp,#1 :SHL: 26                    ;last index done if index field wrapped to 0
        BHS %BT01
        ADD $temp,$temp,#1 :SHL: 4               ;next segment
        CMP $temp,#8 :SHL: 4                     ;8 segments done?
        BLO %BT01
        MEND

  ]

;flush whole TLB (actually, same as ARMA_flush_TLBs)
        MACRO
        ARM8_flush_TLB $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_TLB_reg,C7,0
        MEND

;flush TLB entry, virtual address in $reg
        MACRO
        ARM8_flush_TLBentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_TLB_reg,C7,1
        MEND

;select external Refclk pin as fast clock (dynamic switching, asynchronous)
        MACRO
        ARM8_refclk_fclk $temp
        MRC ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        BIC $temp, $temp,#&1                        ;turn off dynamic bus switching (bit0)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        BIC $temp,$temp,#&2                         ;select asynchronous mode (default) (bit1)
        ORR $temp,$temp,#&4                         ;select REFCLK as the FCLK source (bits3:2)
        BIC $temp,$temp,#&10                        ;ensure L=0 when writing (PLL locked) (bit4)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        NOP
        NOP
        NOP
        NOP
        ORR $temp,$temp,#&1                         ;select dynamic clock switching (bit0)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        MEND

;select PLL output as fast clock (dynamic switching, asynchronous)
        MACRO
        ARM8_pll_fclk $temp
        MRC ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        BIC $temp,$temp,#&1                         ;turn off dynamic bus switching (bit0)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        BIC $temp,$temp,#&2                         ;select asynchronous mode (default) (bit1)
        ORR $temp,$temp,#&C                         ;select PLLClkOut as the FCLK source (bits3:2)
        BIC $temp,$temp,#&10                        ;ensure L=0 when writing (PLL locked) (bit4)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        NOP
        NOP
        NOP
        NOP
        ORR $temp,$temp,#&1                         ;select dynamic clock switching (bit0)
        MCR ARM_config_cp,0,$temp,ARM8_CTC_reg,C0,0
        MEND

 ] ;ARM810support

;
; -------------- StrongARM only ------------------------------------------
;

;clean whole data cache, using 16k private cleaner area at address in
;$cleanaddr
;trashes $cleanaddr,$temp1,$temp2
;
;method:
;clean whole (16k) data cache by reading 16k private cleaner area in 8-word
;(one cache line) steps
;
;note: this routine should NOT be used as is without care - remember
;      1) interrupts should be off (to guarantee this clean is effective)
;      2) DC should be flushed afterwards (to guarantee next clean using
;         private area is effective, ie. all private area flushed out now)
;      see ARMA_fullycleanflush_DC for 'packaged routine'
;
        MACRO
        ARMA_clean_DC $cleanaddr,$temp1,$temp2
        ADD     $temp1,$cleanaddr,#16*1024
10
        LDR     $temp2,[$cleanaddr],#32
        TEQ     $temp1,$cleanaddr
        BNE     %BT10
        MEND

;flush whole data cache
        MACRO
        ARMA_flush_DC $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_cache_reg,C6,0
        MEND

;clean data cache entry, virtual addr in $reg
        MACRO
        ARMA_clean_DCentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C10,1
        MEND

;flush data cache entry, virtual addr in $reg
        MACRO
        ARMA_flush_DCentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C6,1
        MEND

;clean and flush data cache entry, virtual addr in $reg
        MACRO
        ARMA_cleanflush_DCentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_cache_reg,C14,1
        MEND

;clean data cache for virtual address range from $lo (inclusive) to $hi (exclusive)
;corrupts $lo,$hi
        MACRO
        ARMA_clean_DCrange $lo,$hi
        BIC     $lo,$lo,#31    ;align down to 8-word (1 cache line) boundary
        ADD     $hi,$hi,#31
        BIC     $hi,$hi,#31    ;align up to 8-word boundary
01
        ARMA_clean_DCentry $lo ;clean entry for virtual address $lo
        ADD     $lo,$lo,#32    ;next line
        ARMA_clean_DCentry $lo
        ADD     $lo,$lo,#32
        ARMA_clean_DCentry $lo
        ADD     $lo,$lo,#32
        ARMA_clean_DCentry $lo
        ADD     $lo,$lo,#32
        CMP     $lo,$hi
        BLO     %BT01
        MEND

;drain write buffer
        MACRO
        ARMA_drain_WB $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_cache_reg,C10,4
        MEND

;flush whole instruction cache
        MACRO
        ARMA_flush_IC $WithoutNOPs,$cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_cache_reg,C5,0
      [ "$WithoutNOPs" = ""
        MOV     R0,R0 ; 4 NOPS - up to 4 further instructions may come from IC before flush
        MOV     R0,R0
        MOV     R0,R0
        MOV     R0,R0
      ]
        MEND

;flush whole instruction cache and whole data cache
        MACRO
        ARMA_flush_ICandDC $WithoutNOPs,$cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_cache_reg,C7,0
      [ "$WithoutNOPs" = ""
        MOV     R0,R0 ; 4 NOPS - up to 4 further instructions may come from IC before flush
        MOV     R0,R0
        MOV     R0,R0
        MOV     R0,R0
      ]
        MEND

;flush whole instruction TLB
        MACRO
        ARMA_flush_ITLB $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_TLB_reg,C5,0
        MEND

;flush whole data TLB
        MACRO
        ARMA_flush_DTLB $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_TLB_reg,C6,0
        MEND

;flush whole instruction and data TLBs
        MACRO
        ARMA_flush_TLBs $cond
        MCR$cond ARM_config_cp,0,R0,ARM8A_TLB_reg,C7,0
        MEND

;flush data TLB entry, virtual address in $reg
        MACRO
        ARMA_flush_DTLBentry $reg,$cond
        MCR$cond ARM_config_cp,0,$reg,ARM8A_TLB_reg,C6,1
        MEND

;fully clean and flush DC - see ARMA_clean_DC for more info
;assumed to be 26 bit mode
        MACRO
        ARMA_fullycleanflush_DC $cleanaddr,$temp1,$temp2,$temp3
        MOV     $temp3,pc
        ORR     $temp1,$temp3,#I_bit
        TEQP    $temp1,#0             ;disable IRQs
        ARMA_clean_DC $cleanaddr,$temp1,$temp2
        ARMA_flush_DC
        TEQP    $temp3,#0             ;restore IRQ state
        MEND

;enable core clock switching (fast core clock allowed)
        MACRO
        ARMA_fastcoreclock $cond
        MCR$cond ARM_config_cp,0,R0,ARMA_TCI_reg,C1,2
        MEND

;disable core clock switching (core clock is memory clock)
        MACRO
        ARMA_slowcoreclock $cond
        MCR$cond ARM_config_cp,0,R0,ARMA_TCI_reg,C2,2
        MEND


    END