2 * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #define pr_fmt(fmt) "uniphier: " fmt
17 #include <linux/init.h>
19 #include <linux/ioport.h>
21 #include <linux/of_address.h>
22 #include <linux/sizes.h>
23 #include <asm/cacheflush.h>
24 #include <asm/hardware/cache-uniphier.h>
25 #include <asm/pgtable.h>
27 #include <asm/smp_scu.h>
30 * The secondary CPUs check this register from the boot ROM for the jump
31 * destination. After that, it can be reused as a scratch register.
33 #define UNIPHIER_SMPCTRL_ROM_RSV2 0x208
35 static void __iomem
*uniphier_smp_rom_boot_rsv2
;
36 static unsigned int uniphier_smp_max_cpus
;
38 extern char uniphier_smp_trampoline
;
39 extern char uniphier_smp_trampoline_jump
;
40 extern char uniphier_smp_trampoline_poll_addr
;
41 extern char uniphier_smp_trampoline_end
;
44 * Copy trampoline code to the tail of the 1st section of the page table used
45 * in the boot ROM. This area is directly accessible by the secondary CPUs
46 * for all the UniPhier SoCs.
48 static const phys_addr_t uniphier_smp_trampoline_dest_end
= SECTION_SIZE
;
49 static phys_addr_t uniphier_smp_trampoline_dest
;
51 static int __init
uniphier_smp_copy_trampoline(phys_addr_t poll_addr
)
54 static void __iomem
*trmp_base
;
56 if (!uniphier_cache_l2_is_enabled()) {
57 pr_warn("outer cache is needed for SMP, but not enabled\n");
61 uniphier_cache_l2_set_locked_ways(1);
65 trmp_size
= &uniphier_smp_trampoline_end
- &uniphier_smp_trampoline
;
66 uniphier_smp_trampoline_dest
= uniphier_smp_trampoline_dest_end
-
69 uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest
,
70 uniphier_smp_trampoline_dest_end
);
72 trmp_base
= ioremap_cache(uniphier_smp_trampoline_dest
, trmp_size
);
74 pr_err("failed to map trampoline destination area\n");
78 memcpy(trmp_base
, &uniphier_smp_trampoline
, trmp_size
);
80 writel(virt_to_phys(secondary_startup
),
81 trmp_base
+ (&uniphier_smp_trampoline_jump
-
82 &uniphier_smp_trampoline
));
84 writel(poll_addr
, trmp_base
+ (&uniphier_smp_trampoline_poll_addr
-
85 &uniphier_smp_trampoline
));
87 flush_cache_all(); /* flush out trampoline code to outer cache */
94 static int __init
uniphier_smp_prepare_trampoline(unsigned int max_cpus
)
96 struct device_node
*np
;
98 phys_addr_t rom_rsv2_phys
;
101 np
= of_find_compatible_node(NULL
, NULL
, "socionext,uniphier-smpctrl");
103 ret
= of_address_to_resource(np
, 0, &res
);
105 rom_rsv2_phys
= res
.start
+ UNIPHIER_SMPCTRL_ROM_RSV2
;
107 /* try old binding too */
108 np
= of_find_compatible_node(NULL
, NULL
,
109 "socionext,uniphier-system-bus-controller");
111 ret
= of_address_to_resource(np
, 1, &res
);
113 pr_err("failed to get resource of SMP control\n");
116 rom_rsv2_phys
= res
.start
+ 0x1000 + UNIPHIER_SMPCTRL_ROM_RSV2
;
119 ret
= uniphier_smp_copy_trampoline(rom_rsv2_phys
);
123 uniphier_smp_rom_boot_rsv2
= ioremap(rom_rsv2_phys
, sizeof(SZ_4
));
124 if (!uniphier_smp_rom_boot_rsv2
) {
125 pr_err("failed to map ROM_BOOT_RSV2 register\n");
129 writel(uniphier_smp_trampoline_dest
, uniphier_smp_rom_boot_rsv2
);
130 asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
132 uniphier_smp_max_cpus
= max_cpus
; /* save for later use */
137 static void __init
uniphier_smp_unprepare_trampoline(void)
139 iounmap(uniphier_smp_rom_boot_rsv2
);
141 if (uniphier_smp_trampoline_dest
)
142 outer_inv_range(uniphier_smp_trampoline_dest
,
143 uniphier_smp_trampoline_dest_end
);
145 uniphier_cache_l2_set_locked_ways(0);
148 static int __init
uniphier_smp_enable_scu(void)
150 unsigned long scu_base_phys
= 0;
151 void __iomem
*scu_base
;
153 if (scu_a9_has_base())
154 scu_base_phys
= scu_a9_get_base();
156 if (!scu_base_phys
) {
157 pr_err("failed to get scu base\n");
161 scu_base
= ioremap(scu_base_phys
, SZ_128
);
163 pr_err("failed to map scu base\n");
167 scu_enable(scu_base
);
173 static void __init
uniphier_smp_prepare_cpus(unsigned int max_cpus
)
175 static cpumask_t only_cpu_0
= { CPU_BITS_CPU0
};
178 ret
= uniphier_smp_prepare_trampoline(max_cpus
);
182 ret
= uniphier_smp_enable_scu();
188 pr_warn("disabling SMP\n");
189 init_cpu_present(&only_cpu_0
);
190 uniphier_smp_unprepare_trampoline();
193 static int __init
uniphier_smp_boot_secondary(unsigned int cpu
,
194 struct task_struct
*idle
)
196 if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2
))
199 writel(cpu
, uniphier_smp_rom_boot_rsv2
);
200 readl(uniphier_smp_rom_boot_rsv2
); /* relax */
202 asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
204 if (cpu
== uniphier_smp_max_cpus
- 1) {
205 /* clean up resources if this is the last CPU */
206 uniphier_smp_unprepare_trampoline();
212 static const struct smp_operations uniphier_smp_ops __initconst
= {
213 .smp_prepare_cpus
= uniphier_smp_prepare_cpus
,
214 .smp_boot_secondary
= uniphier_smp_boot_secondary
,
216 CPU_METHOD_OF_DECLARE(uniphier_smp
, "socionext,uniphier-smp",