1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/mach-axxia/platsmp.c |
4 | * |
5 | * Copyright (C) 2012 LSI Corporation |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/io.h> |
10 | #include <linux/smp.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | #include <asm/cacheflush.h> |
14 | |
15 | /* Syscon register offsets for releasing cores from reset */ |
16 | #define SC_CRIT_WRITE_KEY 0x1000 |
17 | #define SC_RST_CPU_HOLD 0x1010 |
18 | |
19 | /* |
20 | * Write the kernel entry point for secondary CPUs to the specified address |
21 | */ |
22 | static void write_release_addr(u32 release_phys) |
23 | { |
24 | u32 *virt = (u32 *) phys_to_virt(address: release_phys); |
25 | writel_relaxed(__pa_symbol(secondary_startup), virt); |
26 | /* Make sure this store is visible to other CPUs */ |
27 | smp_wmb(); |
28 | __cpuc_flush_dcache_area(virt, sizeof(u32)); |
29 | } |
30 | |
31 | static int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) |
32 | { |
33 | struct device_node *syscon_np; |
34 | void __iomem *syscon; |
35 | u32 tmp; |
36 | |
37 | syscon_np = of_find_compatible_node(NULL, NULL, compat: "lsi,axxia-syscon" ); |
38 | if (!syscon_np) |
39 | return -ENOENT; |
40 | |
41 | syscon = of_iomap(node: syscon_np, index: 0); |
42 | of_node_put(node: syscon_np); |
43 | if (!syscon) |
44 | return -ENOMEM; |
45 | |
46 | tmp = readl(addr: syscon + SC_RST_CPU_HOLD); |
47 | writel(val: 0xab, addr: syscon + SC_CRIT_WRITE_KEY); |
48 | tmp &= ~(1 << cpu); |
49 | writel(val: tmp, addr: syscon + SC_RST_CPU_HOLD); |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) |
55 | { |
56 | int cpu_count = 0; |
57 | int cpu; |
58 | |
59 | /* |
60 | * Initialise the present map, which describes the set of CPUs actually |
61 | * populated at the present time. |
62 | */ |
63 | for_each_possible_cpu(cpu) { |
64 | struct device_node *np; |
65 | u32 release_phys; |
66 | |
67 | np = of_get_cpu_node(cpu, NULL); |
68 | if (!np) |
69 | continue; |
70 | if (of_property_read_u32(np, propname: "cpu-release-addr" , out_value: &release_phys)) |
71 | continue; |
72 | |
73 | if (cpu_count < max_cpus) { |
74 | set_cpu_present(cpu, present: true); |
75 | cpu_count++; |
76 | } |
77 | |
78 | if (release_phys != 0) |
79 | write_release_addr(release_phys); |
80 | } |
81 | } |
82 | |
83 | static const struct smp_operations axxia_smp_ops __initconst = { |
84 | .smp_prepare_cpus = axxia_smp_prepare_cpus, |
85 | .smp_boot_secondary = axxia_boot_secondary, |
86 | }; |
87 | CPU_METHOD_OF_DECLARE(axxia_smp, "lsi,syscon-release" , &axxia_smp_ops); |
88 | |