powermac-rom/NanoKernel/NKCache.s
Elliot Nunn c7d4cdd367 Reverse power management functions
This is part of the work to get the mini running well. The
kcPowerDispatch and kcCacheDispatch entry points were explored. The dead
code implementing a Timer "Heartbeat" was also used to label some
structures better.
2018-03-17 20:24:02 +08:00

530 lines
12 KiB
ArmAsm

###### ### ###### ## ## ######## ###### ### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ######### ###### ## ## ## ## ##
## ######### ## ## ## ## ## ######### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
###### ## ## ###### ## ## ######## ###### ## ## ######## ########
; Enable/disable/probe the L1/2 data/inst cache
; Probably called using an unknown 68k F-trap. Not usually called on my
; G4, but can be tested by hacking the MPCall table. Uses fancy new CPU
; features (MSSCR0), so probably not legacy code. For CPU accelerator
; cards? `FlushCache` needs to be nopped out to prevent a crash.
; ARGUMENT (r3)
; r3.hi = action flags
; enable specified caches $8000
; disable specified caches $4000
; report pre-change state $2000
; also enable (???) $1000
; enable/disable I-cache $0800
; enable/disable D-cache $0400
;
; r3.lo = which cache (L1/2)
; level 1 1
; level 2 2
;
; RETURN VALUE (r3)
; r3.hi = pre-change state flags (resemble action flags)
; both caches disabled $4000
; either cache enabled $8000
; I-cache enabled $0800
; D-cache enabled $0400
;
; r3.lo = return status
; success 0
; failure < 0
; checked L1 but did not set 1
; checked L2 but did not set 2
; DeclareMPCall 199, kcCacheDispatch ; DEBUG
kcCacheDispatch
_RegRangeToContextBlock r21, r23 ; get some breathing room
; _log 'kcCacheDispatch ' ; DEBUG
; mr r8, r3 ; DEBUG
; bl printw ; DEBUG
; _log '^n' ; DEBUG
clrlwi r8, r3, 16 ; bad selector
cmplwi r8, 2
bgt- @fail_bad_selector
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
andi. r8, r8, 1 << NKProcessorInfo.hasL2CR
beq- CacheCallFailNoL2 ; no L2CR => fail (what about 601?)
rlwinm. r9, r3, 0, 2, 2 ; if flagged, get cache state in r23
bnel- CacheCallGetInfoForReturnValue ; (otherwise, r23 is undefined)
srwi r8, r3, 30 ; cannot enable *and* disable
cmpwi r8, 3
beq- CacheCallFailBadFlags
clrlwi r8, r3, 16 ; go to main code for level 1/2 cache
cmplwi r8, 1
beq- CacheCallDispatchL1
cmplwi r8, 2
beq- CacheCallDispatchL2
@fail_bad_selector ; fall through => bad selector
lisori r3, -2
b CacheCallReturn
### ## ## ###
## ## #### ##
## ## ## ##
## ## ## ##
## ## ## ##
## ## ## ##
### ######## ###### ###
CacheCallDispatchL1
rlwinm. r9, r3, 0, 1, 1
bne- CacheCallL1DisableSelected
rlwinm. r9, r3, 0, 0, 0
bne- CacheCallL1EnableSelected
rlwinm. r9, r3, 0, 3, 3 ; ???
bl FlushCaches
b CacheCallReturn
CacheCallL1DisableSelected
bl FlushCaches
rlwinm r22, r3, 0, 4, 5 ; shift arg bits to align with HID0[DCE/ICE]
srwi r22, r22, 12
mfspr r21, hid0
andc r21, r21, r22 ; HID0 &= ~mybits
sync
mtspr hid0, r21
li r3, 0
b CacheCallReturn
CacheCallL1EnableSelected
rlwinm r22, r3, 0, 4, 5 ; shift arg bits to align with HID0[DCE/ICE]
srwi r22, r22, 12
mfspr r21, hid0
or r21, r21, r22 ; HID0 |= mybits
sync
mtspr hid0, r21
li r3, 0
b CacheCallReturn
### ## ####### ###
## ## ## ## ##
## ## ## ##
## ## ####### ##
## ## ## ##
## ## ## ##
### ######## ######### ###
CacheCallDispatchL2
rlwinm. r9, r3, 0, 1, 1
bne- CacheCallL2DisableSelected
rlwinm. r9, r3, 0, 0, 0
bne- CacheCallL2EnableSelected
rlwinm. r9, r3, 0, 3, 3
bne- CacheCallL2Flag3 ; goes to DisableSelected
rlwinm. r9, r3, 0, 2, 2
;bne removed?
bne- CacheCallReturn
CacheCallFailBadFlags
lisori r3, -4
b CacheCallReturn
CacheCallL2Flag3
bl CacheCallL2DisableSelected ; typo? should be `b`
CacheCallL2EnableSelected
mfspr r21, l2cr ; fail if L2CR[L2E] already set
sync
andis. r21, r21, 0x8000
bne- CacheCallReturn
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8
beq- CacheCallFailNoL2 ; fail if zero-sized cache reported
mfspr r21, hid0 ; save HID0
rlwinm r8, r21, 0, 12, 10 ; clear HID0[DPM] (dynamic power management)
mtspr hid0, r8 ; presumably to keep L2 working while we wait?
sync
addi r8, r1, PSA.ProcessorState
lwz r8, NKProcessorState.saveL2CR(r8)
and. r8, r8, r8
beq- CacheCallReturn ; fail if zero L2CR was saved?
sync
lis r9, 0x0020 ; set L2CR[GI] (global invalidate)
or r8, r8, r9
mtspr l2cr, r8
sync
@inval_loop
mfspr r8, l2cr ; check L2CR[IP] (invalidate progress)
sync
andi. r9, r8, 1
bne+ @inval_loop
lis r9, 0x0020 ; clear L2CR[GI]
andc r8, r8, r9
mtspr l2cr, r8
sync
lis r9, 0x8000 ; set L2CR[L2E] (L2 enable)
or r8, r8, r9
mtspr l2cr, r8
sync
mtspr hid0, r21 ; restore HID0
sync
li r3, 0 ; return successfully
b CacheCallReturn
CacheCallFailNoL2
li r3, -2
b CacheCallReturn
CacheCallL2DisableSelected
mfspr r22, l2cr ; return if already disabled per L2CR[L2E]
sync
andis. r22, r22, 0x8000
beq- CacheCallReturn
bl FlushCaches
mfspr r22, l2cr ; clear L2CR[L2E]
sync
clrlwi r22, r22, 1
mtspr l2cr, r22
sync
addi r8, r1, PSA.ProcessorState
stw r22, NKProcessorState.saveL2CR(r8) ; update saveL2CR
sync
rlwinm r22, r22, 0, 7, 3 ; clear L2CR[3/5/6] (all reserved)
oris r22, r22, 0x0010 ; set L2CR[13] (also reserved)
mtspr l2cr, r22
sync
;b CacheCallReturn ; fall through
### ######## ######## ######## ## ## ######## ## ## ###
## ## ## ## ## ## ## ## ## ### ## ##
## ## ## ## ## ## ## ## ## #### ## ##
## ######## ###### ## ## ## ######## ## ## ## ##
## ## ## ## ## ## ## ## ## ## #### ##
## ## ## ## ## ## ## ## ## ## ### ##
### ## ## ######## ## ####### ## ## ## ## ###
CacheCallReturn
ori r23, r23, 0xffff ; put the r23.hi from CacheCallGetInfoForReturnValue into r3.hi
oris r3, r3, 0xffff
and r3, r3, r23
CacheCallReturnWithoutFlags
_RegRangeFromContextBlock r21, r23
sync
; _log 'Return ' ; DEBUG
; mr r8, r3 ; DEBUG
; bl printw ; DEBUG
; _log '^n' ; DEBUG
b IntReturn
### ######## ######## ####### ######## ######## ###
## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ##
## ######## ######## ## ## ######## ###### ##
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ##
### ## ## ## ####### ######## ######## ###
; RET r23.hi = flags describing state of specified cache (see top of file)
CacheCallGetInfoForReturnValue
clrlwi r8, r3, 16
cmplwi r8, 1
beq- @level1
cmplwi r8, 2
beq- @level2
lisori r3, -5
b CacheCallReturnWithoutFlags
@level1
mfspr r21, hid0
rlwinm. r21, r21, 12, 4, 5
beq- @all_off
oris r23, r21, 0x8000
blr
@level2
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8
beq+ CacheCallFailNoL2
mfspr r21, hid0 ; same bits as above
rlwinm r21, r21, 12, 4, 5
mfspr r22, l2cr ; L2-D is on if L1-D is on and L2CR[DO] is cleared
rlwinm r22, r22, 5, 4, 4
andc r21, r21, r22
mfspr r22, l2cr ; then again, both L2s are off if L2CR[L2E] is cleared
andis. r22, r22, 0x8000
beq- @all_off
or r23, r21, r22
blr
@all_off
lisori r23, 0x40000000
blr
######## ## ## ## ###### ## ## ######## ## ## ## ## ###### ######
## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## #### ## ## ##
###### ## ## ## ###### ######### ###### ## ## ## ## ## ## ######
## ## ## ## ## ## ## ## ## ## ## #### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ##
## ######## ####### ###### ## ## ## ####### ## ## ###### ######
; Flush L1 and L2 caches
; Also used by NKPowerCalls.s
; ARG KDP *r1, ContextBlock *r6
; CLOB r8, r9, cr
FlushCaches
; blr ; DEBUG
; Be cautious
mfctr r8
stw r25, ContextBlock.r25(r6)
stw r24, ContextBlock.r24(r6)
stw r8, ContextBlock.KernelCTR(r6)
; Flush level 1
lhz r25, KDP.ProcessorInfo + NKProcessorInfo.DataCacheLineSize(r1)
and. r25, r25, r25 ; r25 = L1-D line size
cntlzw r8, r25
beq- @return
subfic r9, r8, 31 ; r9 = logb(L1-D line size)
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.DataCacheTotalSize(r1)
and. r8, r8, r8 ; r8 = L1-D size
beq- @return
lwz r24, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
mtcr r24
bc BO_IF, 31 - NKProcessorInfo.hasMSSregs, @use_SPRs_to_invalidate
; => go away to handle weird CPUs
bc BO_IF_NOT, 31 - NKProcessorInfo.hasPLRUL1, @no_pseudo_lru
slwi r24, r8, 1
add r8, r8, r24
srwi r8, r8, 1 ; be generous with pseudo-LRU caches
@no_pseudo_lru
srw r8, r8, r9
mtctr r8 ; loop counter = cache/line
lwz r8, KDP.PA_ConfigInfo(r1) ; fill the cache with Mac ROM
lwz r9, NKConfigurationInfo.ROMImageBaseOffset(r8)
add r8, r8, r9
@loop_L1
lwzux r9, r8, r25
bdnz+ @loop_L1
; Flush level 2 (very similar to above)
lwz r24, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
andi. r24, r24, 1 << NKProcessorInfo.hasL2CR
beq- @return ; return if L2CR unavailable
mfspr r24, l2cr
andis. r24, r24, 0x8000
beq- @return ; return if L2 off (per L2CR[L2E])
lhz r25, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DBlockSize(r1)
and. r25, r25, r25 ; r25 = L2-D line size
cntlzw r8, r25
beq- @return
subfic r9, r8, 31 ; r9 = logb(L2-D line size)
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8 ; r8 = L2-D size
beq- @return
srw r8, r8, r9
mtctr r8 ; loop counter = cache/line
mfspr r24, l2cr ; set L2CR[DO] (disables L2-I)
oris r24, r24, 0x0040
mtspr l2cr, r24
isync
lwz r8, KDP.PA_ConfigInfo(r1) ; fill the cache with Mac ROM
lwz r9, NKConfigurationInfo.ROMImageBaseOffset(r8)
add r8, r8, r9
addis r8, r8, 0x19 ; start high in ROM and count backwards
neg r25, r25
@loop_L2
lwzux r9, r8, r25
bdnz+ @loop_L2
rlwinm r24, r24, 0, 10, 8
mtspr l2cr, r24 ; clear L2CR[DO] (reenables L2-I)
isync
; Done (this return path is also called from the sneaky code below)
@return
lwz r8, ContextBlock.KernelCTR(r6)
lwz r25, ContextBlock.r25(r6)
lwz r24, ContextBlock.r24(r6)
sync
mtctr r8
blr
; If "hasMSSregs" flag (my name) is set in ProcessorFlags, L1 and L2 can
; instead be flushed by clobbering reserved bits in MSSCR0 and L2CR
; respectively.
@use_SPRs_to_invalidate
; Flush level 1: set MSSCR0[8] and spin until it clears
dssall ; AltiVec needs to know
sync
mfspr r8, msscr0
oris r8, r8, 0x0080
mtspr msscr0, r8
sync
@loop_msscr0
mfspr r8, msscr0
sync
andis. r8, r8, 0x0080
bne+ @loop_msscr0
; Flush level 2: set L2CR[4] and spin until it clears
mfspr r8, l2cr
ori r8, r8, 0x0800
mtspr l2cr, r8
sync
@loop_l2cr
mfspr r8, l2cr
sync
andi. r8, r8, 0x0800
bne+ @loop_l2cr
; Jump back up to main code path to return
b @return
; Called when we cop a machine check with the "L1 data cache error"
; flag set in SRR1, followed by an interrupt return. Same trick as
; above.
; CLOB r8, cr
FlushL1CacheUsingMSSCR0
; Return if MSSCR0 unavailable
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
mtcr r8
bclr BO_IF_NOT, 31-NKProcessorInfo.hasMSSregs
; Flush level 1: set MSSCR0[8] and spin until it clears
dssall ; AltiVec needs to know
sync
mfspr r8, msscr0
oris r8, r8, 0x0080
mtspr msscr0, r8
sync
@loop_msscr0
mfspr r8, msscr0
sync
andis. r8, r8, 0x0080
bne+ @loop_msscr0
blr