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