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