xref: /arm-trusted-firmware/plat/allwinner/common/arisc_off.S (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
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