Merge tag 'iwlwifi-next-for-kalle-2016-07-06' of git://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / arch / arm / mach-uniphier / platsmp.c
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_SMPCTRL_ROM_RSV2       0x208
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
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
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, "socionext,uniphier-smpctrl");
102         ret = of_address_to_resource(np, 0, &res);
103         of_node_put(np);
104         if (!ret) {
105                 rom_rsv2_phys = res.start + UNIPHIER_SMPCTRL_ROM_RSV2;
106         } else {
107                 /* try old binding too */
108                 np = of_find_compatible_node(NULL, NULL,
109                                              "socionext,uniphier-system-bus-controller");
110                 ret = of_address_to_resource(np, 1, &res);
111                 of_node_put(np);
112                 if (ret) {
113                         pr_err("failed to get resource of SMP control\n");
114                         return ret;
115                 }
116                 rom_rsv2_phys = res.start + 0x1000 + UNIPHIER_SMPCTRL_ROM_RSV2;
117         }
118
119         ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
120         if (ret)
121                 return ret;
122
123         uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, SZ_4);
124         if (!uniphier_smp_rom_boot_rsv2) {
125                 pr_err("failed to map ROM_BOOT_RSV2 register\n");
126                 return -ENOMEM;
127         }
128
129         writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
130         asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
131
132         uniphier_smp_max_cpus = max_cpus;       /* save for later use */
133
134         return 0;
135 }
136
137 static void __init uniphier_smp_unprepare_trampoline(void)
138 {
139         iounmap(uniphier_smp_rom_boot_rsv2);
140
141         if (uniphier_smp_trampoline_dest)
142                 outer_inv_range(uniphier_smp_trampoline_dest,
143                                 uniphier_smp_trampoline_dest_end);
144
145         uniphier_cache_l2_set_locked_ways(0);
146 }
147
148 static int __init uniphier_smp_enable_scu(void)
149 {
150         unsigned long scu_base_phys = 0;
151         void __iomem *scu_base;
152
153         if (scu_a9_has_base())
154                 scu_base_phys = scu_a9_get_base();
155
156         if (!scu_base_phys) {
157                 pr_err("failed to get scu base\n");
158                 return -ENODEV;
159         }
160
161         scu_base = ioremap(scu_base_phys, SZ_128);
162         if (!scu_base) {
163                 pr_err("failed to map scu base\n");
164                 return -ENOMEM;
165         }
166
167         scu_enable(scu_base);
168         iounmap(scu_base);
169
170         return 0;
171 }
172
173 static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
174 {
175         static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
176         int ret;
177
178         ret = uniphier_smp_prepare_trampoline(max_cpus);
179         if (ret)
180                 goto err;
181
182         ret = uniphier_smp_enable_scu();
183         if (ret)
184                 goto err;
185
186         return;
187 err:
188         pr_warn("disabling SMP\n");
189         init_cpu_present(&only_cpu_0);
190         uniphier_smp_unprepare_trampoline();
191 }
192
193 static int __init uniphier_smp_boot_secondary(unsigned int cpu,
194                                               struct task_struct *idle)
195 {
196         if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
197                 return -EFAULT;
198
199         writel(cpu, uniphier_smp_rom_boot_rsv2);
200         readl(uniphier_smp_rom_boot_rsv2); /* relax */
201
202         asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
203
204         if (cpu == uniphier_smp_max_cpus - 1) {
205                 /* clean up resources if this is the last CPU */
206                 uniphier_smp_unprepare_trampoline();
207         }
208
209         return 0;
210 }
211
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,
215 };
216 CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
217                       &uniphier_smp_ops);