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