Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/microwatt: Add SMP support

This adds support for Microwatt systems with more than one core, and
updates the device tree for a 2-core version.

The secondary CPUs are started and sent to spin in __secondary_hold
very early on, in the platform probe function. The reason for doing
this is so that they are there when smp_release_cpus() gets called,
which is before the platform init_smp function or even the platform
setup_arch function gets called.

Note that having two CPUs in the device tree doesn't preclude
operation with only one CPU. The SYSCON_CPU_CTRL register has a
read-only field which indicates the number of CPU cores, so
microwatt_init_smp() will only start as many CPU cores as are present
in the system, and any extra CPU device-tree nodes will just be
ignored.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Link: https://patch.msgid.link/Z5xt8aooKyXZv6Kf@thinks.paulus.ozlabs.org

authored by

Paul Mackerras and committed by
Madhavan Srinivasan
aca95fb6 3d45a3d0

+124 -3
+32 -2
arch/powerpc/boot/dts/microwatt.dts
··· 142 142 ibm,mmu-lpid-bits = <12>; 143 143 ibm,mmu-pid-bits = <20>; 144 144 }; 145 + 146 + PowerPC,Microwatt@1 { 147 + i-cache-sets = <2>; 148 + ibm,dec-bits = <64>; 149 + reservation-granule-size = <64>; 150 + clock-frequency = <100000000>; 151 + timebase-frequency = <100000000>; 152 + i-tlb-sets = <1>; 153 + ibm,ppc-interrupt-server#s = <1>; 154 + i-cache-block-size = <64>; 155 + d-cache-block-size = <64>; 156 + d-cache-sets = <2>; 157 + i-tlb-size = <64>; 158 + cpu-version = <0x990000>; 159 + status = "okay"; 160 + i-cache-size = <0x1000>; 161 + ibm,processor-radix-AP-encodings = <0x0c 0xa0000010 0x20000015 0x4000001e>; 162 + tlb-size = <0>; 163 + tlb-sets = <0>; 164 + device_type = "cpu"; 165 + d-tlb-size = <128>; 166 + d-tlb-sets = <2>; 167 + reg = <1>; 168 + general-purpose; 169 + 64-bit; 170 + d-cache-size = <0x1000>; 171 + ibm,chip-id = <0>; 172 + ibm,mmu-lpid-bits = <12>; 173 + ibm,mmu-pid-bits = <20>; 174 + }; 145 175 }; 146 176 147 177 soc@c0000000 { ··· 184 154 185 155 interrupt-controller@4000 { 186 156 compatible = "openpower,xics-presentation", "ibm,ppc-xicp"; 187 - ibm,interrupt-server-ranges = <0x0 0x1>; 188 - reg = <0x4000 0x100>; 157 + ibm,interrupt-server-ranges = <0x0 0x2>; 158 + reg = <0x4000 0x10 0x4010 0x10>; 189 159 }; 190 160 191 161 ICS: interrupt-controller@5000 {
+1 -1
arch/powerpc/platforms/microwatt/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 config PPC_MICROWATT 3 - depends on PPC_BOOK3S_64 && !SMP 3 + depends on PPC_BOOK3S_64 4 4 bool "Microwatt SoC platform" 5 5 select PPC_XICS 6 6 select PPC_ICS_NATIVE
+1
arch/powerpc/platforms/microwatt/Makefile
··· 1 1 obj-y += setup.o rng.o 2 + obj-$(CONFIG_SMP) += smp.o
+1
arch/powerpc/platforms/microwatt/microwatt.h
··· 3 3 #define _MICROWATT_H 4 4 5 5 void microwatt_rng_init(void); 6 + void microwatt_init_smp(void); 6 7 7 8 #endif /* _MICROWATT_H */
+9
arch/powerpc/platforms/microwatt/setup.c
··· 29 29 } 30 30 machine_arch_initcall(microwatt, microwatt_populate); 31 31 32 + static int __init microwatt_probe(void) 33 + { 34 + /* Main reason for having this is to start the other CPU(s) */ 35 + if (IS_ENABLED(CONFIG_SMP)) 36 + microwatt_init_smp(); 37 + return 1; 38 + } 39 + 32 40 static void __init microwatt_setup_arch(void) 33 41 { 34 42 microwatt_rng_init(); ··· 53 45 define_machine(microwatt) { 54 46 .name = "microwatt", 55 47 .compatible = "microwatt-soc", 48 + .probe = microwatt_probe, 56 49 .init_IRQ = microwatt_init_IRQ, 57 50 .setup_arch = microwatt_setup_arch, 58 51 .progress = udbg_progress,
+80
arch/powerpc/platforms/microwatt/smp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + /* 4 + * SMP support functions for Microwatt 5 + * Copyright 2025 Paul Mackerras <paulus@ozlabs.org> 6 + */ 7 + 8 + #include <linux/kernel.h> 9 + #include <linux/smp.h> 10 + #include <linux/io.h> 11 + #include <asm/early_ioremap.h> 12 + #include <asm/ppc-opcode.h> 13 + #include <asm/reg.h> 14 + #include <asm/smp.h> 15 + #include <asm/xics.h> 16 + 17 + #include "microwatt.h" 18 + 19 + static void __init microwatt_smp_probe(void) 20 + { 21 + xics_smp_probe(); 22 + } 23 + 24 + static void microwatt_smp_setup_cpu(int cpu) 25 + { 26 + if (cpu != 0) 27 + xics_setup_cpu(); 28 + } 29 + 30 + static struct smp_ops_t microwatt_smp_ops = { 31 + .probe = microwatt_smp_probe, 32 + .message_pass = NULL, /* Use smp_muxed_ipi_message_pass */ 33 + .kick_cpu = smp_generic_kick_cpu, 34 + .setup_cpu = microwatt_smp_setup_cpu, 35 + }; 36 + 37 + /* XXX get from device tree */ 38 + #define SYSCON_BASE 0xc0000000 39 + #define SYSCON_LENGTH 0x100 40 + 41 + #define SYSCON_CPU_CTRL 0x58 42 + 43 + void __init microwatt_init_smp(void) 44 + { 45 + volatile unsigned char __iomem *syscon; 46 + int ncpus; 47 + int timeout; 48 + 49 + syscon = early_ioremap(SYSCON_BASE, SYSCON_LENGTH); 50 + if (syscon == NULL) { 51 + pr_err("Failed to map SYSCON\n"); 52 + return; 53 + } 54 + ncpus = (readl(syscon + SYSCON_CPU_CTRL) >> 8) & 0xff; 55 + if (ncpus < 2) 56 + goto out; 57 + 58 + smp_ops = &microwatt_smp_ops; 59 + 60 + /* 61 + * Write two instructions at location 0: 62 + * mfspr r3, PIR 63 + * b __secondary_hold 64 + */ 65 + *(unsigned int *)KERNELBASE = PPC_RAW_MFSPR(3, SPRN_PIR); 66 + *(unsigned int *)(KERNELBASE+4) = PPC_RAW_BRANCH(&__secondary_hold - (char *)(KERNELBASE+4)); 67 + 68 + /* enable the other CPUs, they start at location 0 */ 69 + writel((1ul << ncpus) - 1, syscon + SYSCON_CPU_CTRL); 70 + 71 + timeout = 10000; 72 + while (!__secondary_hold_acknowledge) { 73 + if (--timeout == 0) 74 + break; 75 + barrier(); 76 + } 77 + 78 + out: 79 + early_iounmap((void *)syscon, SYSCON_LENGTH); 80 + }