xref: /aosp_15_r20/external/arm-trusted-firmware/plat/allwinner/common/arisc_off.S (revision 54fd6939e177f8ff529b10183254802c76df6d08)
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