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