• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3  *
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.
8  *
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.
13  */
14 
15 #define pr_fmt(fmt)		"uniphier: " fmt
16 
17 #include <linux/init.h>
18 #include <linux/io.h>
19 #include <linux/ioport.h>
20 #include <linux/of.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>
26 #include <asm/smp.h>
27 #include <asm/smp_scu.h>
28 
29 /*
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.
32  */
33 #define UNIPHIER_SBC_ROM_BOOT_RSV2	0x1208
34 
35 static void __iomem *uniphier_smp_rom_boot_rsv2;
36 static unsigned int uniphier_smp_max_cpus;
37 
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;
42 
43 /*
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.
47  */
48 static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE;
49 static phys_addr_t uniphier_smp_trampoline_dest;
50 
uniphier_smp_copy_trampoline(phys_addr_t poll_addr)51 static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr)
52 {
53 	size_t trmp_size;
54 	static void __iomem *trmp_base;
55 
56 	if (!uniphier_cache_l2_is_enabled()) {
57 		pr_warn("outer cache is needed for SMP, but not enabled\n");
58 		return -ENODEV;
59 	}
60 
61 	uniphier_cache_l2_set_locked_ways(1);
62 
63 	outer_flush_all();
64 
65 	trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline;
66 	uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end -
67 								trmp_size;
68 
69 	uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest,
70 				      uniphier_smp_trampoline_dest_end);
71 
72 	trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size);
73 	if (!trmp_base) {
74 		pr_err("failed to map trampoline destination area\n");
75 		return -ENOMEM;
76 	}
77 
78 	memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size);
79 
80 	writel(virt_to_phys(secondary_startup),
81 	       trmp_base + (&uniphier_smp_trampoline_jump -
82 			    &uniphier_smp_trampoline));
83 
84 	writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr -
85 				       &uniphier_smp_trampoline));
86 
87 	flush_cache_all();	/* flush out trampoline code to outer cache */
88 
89 	iounmap(trmp_base);
90 
91 	return 0;
92 }
93 
uniphier_smp_prepare_trampoline(unsigned int max_cpus)94 static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus)
95 {
96 	struct device_node *np;
97 	struct resource res;
98 	phys_addr_t rom_rsv2_phys;
99 	int ret;
100 
101 	np = of_find_compatible_node(NULL, NULL,
102 				"socionext,uniphier-system-bus-controller");
103 	ret = of_address_to_resource(np, 1, &res);
104 	if (ret) {
105 		pr_err("failed to get resource of system-bus-controller\n");
106 		return ret;
107 	}
108 
109 	rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2;
110 
111 	ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
112 	if (ret)
113 		return ret;
114 
115 	uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4));
116 	if (!uniphier_smp_rom_boot_rsv2) {
117 		pr_err("failed to map ROM_BOOT_RSV2 register\n");
118 		return -ENOMEM;
119 	}
120 
121 	writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
122 	asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
123 
124 	uniphier_smp_max_cpus = max_cpus;	/* save for later use */
125 
126 	return 0;
127 }
128 
uniphier_smp_unprepare_trampoline(void)129 static void __init uniphier_smp_unprepare_trampoline(void)
130 {
131 	iounmap(uniphier_smp_rom_boot_rsv2);
132 
133 	if (uniphier_smp_trampoline_dest)
134 		outer_inv_range(uniphier_smp_trampoline_dest,
135 				uniphier_smp_trampoline_dest_end);
136 
137 	uniphier_cache_l2_set_locked_ways(0);
138 }
139 
uniphier_smp_enable_scu(void)140 static int __init uniphier_smp_enable_scu(void)
141 {
142 	unsigned long scu_base_phys = 0;
143 	void __iomem *scu_base;
144 
145 	if (scu_a9_has_base())
146 		scu_base_phys = scu_a9_get_base();
147 
148 	if (!scu_base_phys) {
149 		pr_err("failed to get scu base\n");
150 		return -ENODEV;
151 	}
152 
153 	scu_base = ioremap(scu_base_phys, SZ_128);
154 	if (!scu_base) {
155 		pr_err("failed to map scu base\n");
156 		return -ENOMEM;
157 	}
158 
159 	scu_enable(scu_base);
160 	iounmap(scu_base);
161 
162 	return 0;
163 }
164 
uniphier_smp_prepare_cpus(unsigned int max_cpus)165 static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
166 {
167 	static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
168 	int ret;
169 
170 	ret = uniphier_smp_prepare_trampoline(max_cpus);
171 	if (ret)
172 		goto err;
173 
174 	ret = uniphier_smp_enable_scu();
175 	if (ret)
176 		goto err;
177 
178 	return;
179 err:
180 	pr_warn("disabling SMP\n");
181 	init_cpu_present(&only_cpu_0);
182 	uniphier_smp_unprepare_trampoline();
183 }
184 
uniphier_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)185 static int __init uniphier_smp_boot_secondary(unsigned int cpu,
186 					      struct task_struct *idle)
187 {
188 	if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
189 		return -EFAULT;
190 
191 	writel(cpu, uniphier_smp_rom_boot_rsv2);
192 	readl(uniphier_smp_rom_boot_rsv2); /* relax */
193 
194 	asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
195 
196 	if (cpu == uniphier_smp_max_cpus - 1) {
197 		/* clean up resources if this is the last CPU */
198 		uniphier_smp_unprepare_trampoline();
199 	}
200 
201 	return 0;
202 }
203 
204 static struct smp_operations uniphier_smp_ops __initdata = {
205 	.smp_prepare_cpus	= uniphier_smp_prepare_cpus,
206 	.smp_boot_secondary	= uniphier_smp_boot_secondary,
207 };
208 CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
209 		      &uniphier_smp_ops);
210