1# turn_off_core.S 2# 3# Copyright (c) 2018, Andre Przywara <osp@andrep.de> 4# SPDX-License-Identifier: BSD-3-Clause 5# 6# OpenRISC assembly to turn off an ARM core on an Allwinner SoC from 7# the arisc management controller. 8# Generate a binary representation with: 9# $ or1k-elf-as -c -o turn_off_core.o turn_off_core.S 10# $ or1k-elf-objcopy -O binary --reverse-bytes=4 turn_off_core.o \ 11# turn_off_core.bin 12# The encoded instructions go into an array defined in 13# plat/allwinner/sun50i_*/include/core_off_arisc.h, to be handed off to 14# the arisc processor. 15# 16# This routine is meant to be called directly from arisc reset (put the 17# start address in the reset vector), to be actually triggered by that 18# very ARM core to be turned off. 19# It expects the core number presented as a mask in the upper half of 20# r3, so to be patched in the lower 16 bits of the first instruction, 21# overwriting the 0 in this code here. 22# The code will do the following: 23# - Read the C_CPU_STATUS register, which contains the status of the WFI 24# lines of each of the four A53 cores. 25# - Loop until the core in question reaches WFI. 26# - Using that mask, activate the core output clamps by setting the 27# respective core bit in CPUX_PWROFF_GATING_REG (0x1f01500). 28# Note that the clamp for core 0 covers more than just the core, activating 29# it hangs the whole system. So we skip this step for core 0. 30# - Using the negated mask, assert the core's reset line by clearing the 31# respective bit in C_RST_CTRL (0x1f01c30). 32# - Finally turn off the core's power switch by writing 0xff to the 33# respective CPUx_PWR_SWITCH_REG (0x1f01540 ff.) 34# - Assert the arisc's own reset to end execution. 35# This also signals other arisc users that the chip is free again. 36# So in C this would look like: 37# while (!(readl(0x1700030) & (1U << core_nr))) 38# ; 39# if (core_nr != 0) 40# writel(readl(0x1f01500) | (1U << core_nr), 0x1f01500); 41# writel(readl(0x1f01c30) & ~(1U << core_nr), 0x1f01c30); 42# writel(0xff, 0x1f01540 + (core_nr * 4)); 43# (using A64/H5 addresses) 44 45.text 46_start: 47 l.movhi r3, 0 # FIXUP! with core mask 48 l.movhi r0, 0 # clear r0 49 l.movhi r13, 0x170 # r13: CPU_CFG_BASE=0x01700000 50wait_wfi: 51 l.lwz r5, 0x30(r13) # load C_CPU_STATUS 52 l.and r5, r5, r3 # mask requested core 53 l.sfeq r5, r0 # is it not yet in WFI? 54 l.bf wait_wfi # try again 55 56 l.srli r6, r3, 16 # move mask to lower 16 bits 57 l.sfeqi r6, 1 # core 0 is special 58 l.bf 1f # don't touch the bit for core 0 59 l.movhi r13, 0x1f0 # address of R_CPUCFG (delay) 60 l.lwz r5, 0x1500(r13) # core output clamps 61 l.or r5, r5, r6 # set bit to ... 62 l.sw 0x1500(r13), r5 # ... activate for our core 63 641: l.lwz r5, 0x1c30(r13) # CPU power-on reset 65 l.xori r6, r6, -1 # negate core mask 66 l.and r5, r5, r6 # clear bit to ... 67 l.sw 0x1c30(r13), r5 # ... assert for our core 68 69 l.ff1 r6, r3 # get core number from high mask 70 l.addi r6, r6, -17 # convert to 0-3 71 l.slli r6, r6, 2 # r5: core number*4 (0-12) 72 l.add r6, r6, r13 # add to base address 73 l.ori r5, r0, 0xff # 0xff means all switches off 74 l.sw 0x1540(r6), r5 # core power switch registers 75 76reset: l.sw 0x1c00(r13),r0 # pull down our own reset line 77 78 l.j reset # just in case .... 79 l.nop 0x0 # (delay slot) 80 81# same as above, but with the MMIO addresses matching the H6 SoC 82_start_h6: 83 l.movhi r3, 0 # FIXUP! with core mask 84 l.movhi r0, 0 # clear r0 85 l.movhi r13, 0x901 # r13: CPU_CFG_BASE=0x09010000 861: 87 l.lwz r5, 0x80(r13) # load C_CPU_STATUS 88 l.and r5, r5, r3 # mask requested core 89 l.sfeq r5, r0 # is it not yet in WFI? 90 l.bf 1b # try again 91 92 l.srli r6, r3, 16 # move mask to lower 16 bits(ds) 93 l.sfeqi r6, 1 # core 0 is special 94 l.bf 1f # don't touch the bit for core 0 95 l.movhi r13, 0x700 # address of R_CPUCFG (ds) 96 l.lwz r5, 0x0444(r13) # core output clamps 97 l.or r5, r5, r6 # set bit to ... 98 l.sw 0x0444(r13), r5 # ... activate for our core 99 1001: l.lwz r5, 0x0440(r13) # CPU power-on reset 101 l.xori r6, r6, -1 # negate core mask 102 l.and r5, r5, r6 # clear bit to ... 103 l.sw 0x0440(r13), r5 # ... assert for our core 104 105 l.ff1 r6, r3 # get core number from high mask 106 l.addi r6, r6, -17 # convert to 0-3 107 l.slli r6, r6, 2 # r5: core number*4 (0-12) 108 l.add r6, r6, r13 # add to base address 109 l.ori r5, r0, 0xff # 0xff means all switches off 110 l.sw 0x0450(r6), r5 # core power switch registers 111 1121: l.sw 0x0400(r13),r0 # pull down our own reset line 113 114 l.j 1b # just in case ... 115 l.nop 0x0 # (delay slot) 116