The Allwinner H3 chip on the Orange Pi PC has four cores. There are hints in the datasheet, but as it turns out there is not sufficient information there to know how to get things done. I have learned the basic game by looking at the linux kernel code.
Some possibly useful links follow.
I have the linux 4.2.1 kernel sources on my machine and looked at those. This quickly led me to the file arch/arm/mach-sunxi/platsmp.c and the function sun8i_boot_secondary(). The process for starting a core accesses registers in the CPUCFG and PRCM sections of the H3 chip. The base addresses for these are:
CPUCFG_BASE = 0x01f01c00 PRCM_BASE = 0x01f01400(These offsets are specified to the linux kernel in arch/arm/boot/dts/sun8i-a23-a33.dtsi.) The relevant register offsets in these two sections are:
#define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40) #define CPUCFG_GEN_CTRL_REG 0x184 #define CPUCFG_PRIVATE0_REG 0x1a4 #define PRCM_CPU_PWROFF_REG 0x100The "recipe" looks to be the following:
1 - Acquire the CPU spin lock.
2 - Write the address of the code you want the processor to run into the PRIVATE0_REG. (this register is undocumented in the datasheet).
3 - Put the processor into reset by writing 0 to the appropriate processor reset vector. There are 4 of these, one per core. There are two bits (so you write a 3 to de-assert both the "core" and "power on" resets when the time comes.
4 - Reset the L1 cache for that processor by clearing a bit in the general control register. There are other bits, including bits to reset the L2 cache that you don't want to mess with. Do something like this:
GEN_CTRL_REG &= ~BIT(cpu);
5 - Power on the core in question by clearing the power suppression bit. This should look familiar:
PRCM_CPU_PWROFF_REG &= ~BIT(cpu);
6 - De-assert reset.
7 - Release the spin lock.
Looking at the boot rom code, the first question that arises is "what does the following instruction do?"
mrc 15, 0, r0, cr0, cr0, {5}"MRC" is the "move to ARM register from coprocessor" instruction. It is moving from coprocessor 15 (the system coprocessor). The opcode is 0 and it moves to register "r0". Coprocessor register 0 is mentioned twice, Opcode 2 is 5.
Note that this instruction arises in the BE8 macro that is part of the linux assembly language startup when a new core starts running. This is reading the multiprocessor affinity register.
The Allwinner H3 ha a Cortex-A7 processor, which implements the ARMv7-A architecture (not the ARMv7-M). The Cortex-A8 in the BBB also implements the ARMv7-A by the way. Figuring out just what register this is from the 2736 page manual is hell on wheels, but a search for "arm system control register list" led to this register summary. and reveals that this reads the MPIDR (multiprocessor affinity register). The lowest 2 bits tell you what processor (0-3) you are running. The relevant code in the boot rom is:
ffff2c44: ee100fb0 mrc 15, 0, r0, cr0, cr0, {5} ffff2c48: e2001003 and r1, r0, #3 ffff2c4c: e3510000 cmp r1, #0 ffff2c50: 1afffff9 bne 0xffff2c3c ffff2c3c: e59f01c8 ldr r0, [pc, #456] ; 0xffff2e0c (PRIVATE0) ffff2c40: e590f000 ldr pc, [r0]So this is simple and clear. If we are not core 0, we branch to the address in "PRIVATE0". There is more code (and interesting too, but having little to do with the current topic) as follows:
ffff2c44: ee100fb0 mrc 15, 0, r0, cr0, cr0, {5} ffff2c48: e2001003 and r1, r0, #3 ffff2c4c: e3510000 cmp r1, #0 ffff2c50: 1afffff9 bne 0xffff2c3c ; start non-zero core ; We must be core 0 ; This reads the "cluster ID" pin value, which could be ; non-zero is some multiprocessor system with discrete processors. ffff2c54: e2001cff and r1, r0, #65280 ; 0xff00 ffff2c58: e3510000 cmp r1, #0 ffff2c5c: 1afffff6 bne 0xffff2c3c ; Now we read PRIVATE1 and compare it to a "magic value" ; If it matches, we branch to PRIVATE0 ffff2c60: e59f11a8 ldr r1, [pc, #424] ; 0xffff2e10 (PRIVATE1) ffff2c64: e59f21a8 ldr r2, [pc, #424] ; 0xffff2e14 (MAGIC) ffff2c68: e5913000 ldr r3, [r1] ffff2c6c: e1520003 cmp r2, r3 ffff2c70: 1a000000 bne 0xffff2c78 ffff2c74: eafffff0 b 0xffff2c3c ; start as if non-zero core ffff2e0c: 01f01da4 ; address of PRIVATE0 ffff2e10: 01f01dac ; another address (PRIVATE1) ffff2e14: fa50392f ; magic number
Older ARM processors handled the big endian business in a way that caused problems that some folks didn't like. So, ARM fixed this and came out with a new way of doing things. The old way is called BE32 and the new way is called BE8. The transition happened with armv7 (so as of armv7, you get BE8).
Do we care? Probably not, but you trip over this stuff in the linux kernel. In general you want to flip the bit that puts the processor in this mode as early as possible after reset if you are crazy enough to want to go down this road. I don't understand the interest, but there are people putting a lot of effort into this.
Tom's electronics pages / tom@mmto.org