c4692dcd85acd09fea91857d036ef758bc8ab72f
[platform/kernel/u-boot.git] / arch / arm / cpu / armv8 / fsl-layerscape / mp.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2014-2015 Freescale Semiconductor, Inc.
4  */
5
6 #include <common.h>
7 #include <cpu_func.h>
8 #include <image.h>
9 #include <log.h>
10 #include <asm/cache.h>
11 #include <asm/io.h>
12 #include <asm/system.h>
13 #include <asm/arch/mp.h>
14 #include <asm/arch/soc.h>
15 #include <linux/delay.h>
16 #include "cpu.h"
17 #include <asm/arch-fsl-layerscape/soc.h>
18
19 DECLARE_GLOBAL_DATA_PTR;
20
21 void *get_spin_tbl_addr(void)
22 {
23         return &__spin_table;
24 }
25
26 void update_os_arch_secondary_cores(uint8_t os_arch)
27 {
28         u64 *table = get_spin_tbl_addr();
29         int i;
30
31         for (i = 1; i < CONFIG_MAX_CPUS; i++) {
32                 if (os_arch == IH_ARCH_DEFAULT)
33                         table[i * WORDS_PER_SPIN_TABLE_ENTRY +
34                                 SPIN_TABLE_ELEM_ARCH_COMP_IDX] = OS_ARCH_SAME;
35                 else
36                         table[i * WORDS_PER_SPIN_TABLE_ENTRY +
37                                 SPIN_TABLE_ELEM_ARCH_COMP_IDX] = OS_ARCH_DIFF;
38         }
39 }
40
41 #ifdef CONFIG_FSL_LSCH3
42 void wake_secondary_core_n(int cluster, int core, int cluster_cores)
43 {
44         struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
45         struct ccsr_reset __iomem *rst = (void *)(CONFIG_SYS_FSL_RST_ADDR);
46         u32 mpidr = 0;
47
48         mpidr = ((cluster << 8) | core);
49         /*
50          * mpidr_el1 register value of core which needs to be released
51          * is written to scratchrw[6] register
52          */
53         gur_out32(&gur->scratchrw[6], mpidr);
54         asm volatile("dsb st" : : : "memory");
55         rst->brrl |= 1 << ((cluster * cluster_cores) + core);
56         asm volatile("dsb st" : : : "memory");
57         /*
58          * scratchrw[6] register value is polled
59          * when the value becomes zero, this means that this core is up
60          * and running, next core can be released now
61          */
62         while (gur_in32(&gur->scratchrw[6]) != 0)
63                 ;
64 }
65 #endif
66
67 int fsl_layerscape_wake_seconday_cores(void)
68 {
69         struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
70 #ifdef CONFIG_FSL_LSCH3
71         struct ccsr_reset __iomem *rst = (void *)(CONFIG_SYS_FSL_RST_ADDR);
72         u32 svr, ver, cluster, type;
73         int j = 0, cluster_cores = 0;
74 #elif defined(CONFIG_FSL_LSCH2)
75         struct ccsr_scfg __iomem *scfg = (void *)(CONFIG_SYS_FSL_SCFG_ADDR);
76 #endif
77         u32 cores, cpu_up_mask = 1;
78         int i, timeout = 10;
79         u64 *table;
80
81 #ifdef COUNTER_FREQUENCY_REAL
82         /* update for secondary cores */
83         __real_cntfrq = COUNTER_FREQUENCY_REAL;
84         flush_dcache_range((unsigned long)&__real_cntfrq,
85                            (unsigned long)&__real_cntfrq + 8);
86 #endif
87
88         cores = cpu_mask();
89         /* Clear spin table so that secondary processors
90          * observe the correct value after waking up from wfe.
91          */
92         table = get_spin_tbl_addr();
93         memset(table, 0, CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE);
94         flush_dcache_range((unsigned long)table,
95                            (unsigned long)table +
96                            (CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE));
97
98         debug("Waking secondary cores to start from %lx\n", gd->relocaddr);
99
100 #ifdef CONFIG_FSL_LSCH3
101         gur_out32(&gur->bootlocptrh, (u32)(gd->relocaddr >> 32));
102         gur_out32(&gur->bootlocptrl, (u32)gd->relocaddr);
103
104         svr = gur_in32(&gur->svr);
105         ver = SVR_SOC_VER(svr);
106         if (ver == SVR_LS2080A || ver == SVR_LS2085A) {
107                 gur_out32(&gur->scratchrw[6], 1);
108                 asm volatile("dsb st" : : : "memory");
109                 rst->brrl = cores;
110                 asm volatile("dsb st" : : : "memory");
111         } else {
112                 /*
113                  * Release the cores out of reset one-at-a-time to avoid
114                  * power spikes
115                  */
116                 i = 0;
117                 cluster = in_le32(&gur->tp_cluster[i].lower);
118                 for (j = 0; j < TP_INIT_PER_CLUSTER; j++) {
119                         type = initiator_type(cluster, j);
120                         if (type &&
121                             TP_ITYP_TYPE(type) == TP_ITYP_TYPE_ARM)
122                                 cluster_cores++;
123                 }
124
125                 do {
126                         cluster = in_le32(&gur->tp_cluster[i].lower);
127                         for (j = 0; j < TP_INIT_PER_CLUSTER; j++) {
128                                 type = initiator_type(cluster, j);
129                                 if (type &&
130                                     TP_ITYP_TYPE(type) == TP_ITYP_TYPE_ARM)
131                                         wake_secondary_core_n(i, j,
132                                                               cluster_cores);
133                         }
134                 i++;
135                 } while ((cluster & TP_CLUSTER_EOC) != TP_CLUSTER_EOC);
136         }
137 #elif defined(CONFIG_FSL_LSCH2)
138         scfg_out32(&scfg->scratchrw[0], (u32)(gd->relocaddr >> 32));
139         scfg_out32(&scfg->scratchrw[1], (u32)gd->relocaddr);
140         asm volatile("dsb st" : : : "memory");
141         gur_out32(&gur->brrl, cores);
142         asm volatile("dsb st" : : : "memory");
143
144         /* Bootup online cores */
145         scfg_out32(&scfg->corebcr, cores);
146 #endif
147         /* This is needed as a precautionary measure.
148          * If some code before this has accidentally  released the secondary
149          * cores then the pre-bootloader code will trap them in a "wfe" unless
150          * the scratchrw[6] is set. In this case we need a sev here to get these
151          * cores moving again.
152          */
153         asm volatile("sev");
154
155         while (timeout--) {
156                 flush_dcache_range((unsigned long)table, (unsigned long)table +
157                                    CONFIG_MAX_CPUS * 64);
158                 for (i = 1; i < CONFIG_MAX_CPUS; i++) {
159                         if (table[i * WORDS_PER_SPIN_TABLE_ENTRY +
160                                         SPIN_TABLE_ELEM_STATUS_IDX])
161                                 cpu_up_mask |= 1 << i;
162                 }
163                 if (hweight32(cpu_up_mask) == hweight32(cores))
164                         break;
165                 udelay(10);
166         }
167         if (timeout <= 0) {
168                 printf("CPU:   Failed to bring up some cores (mask 0x%x)\n",
169                        cores ^ cpu_up_mask);
170                 return 1;
171         }
172         printf("CPU:   %d cores online\n", hweight32(cores));
173
174         return 0;
175 }
176
177 int is_core_valid(unsigned int core)
178 {
179         return !!((1 << core) & cpu_mask());
180 }
181
182 static int is_pos_valid(unsigned int pos)
183 {
184         return !!((1 << pos) & cpu_pos_mask());
185 }
186
187 int is_core_online(u64 cpu_id)
188 {
189         u64 *table = get_spin_tbl_addr();
190         int pos = id_to_core(cpu_id);
191         table += pos * WORDS_PER_SPIN_TABLE_ENTRY;
192         return table[SPIN_TABLE_ELEM_STATUS_IDX] == 1;
193 }
194
195 int cpu_reset(u32 nr)
196 {
197         puts("Feature is not implemented.\n");
198
199         return 0;
200 }
201
202 int cpu_disable(u32 nr)
203 {
204         puts("Feature is not implemented.\n");
205
206         return 0;
207 }
208
209 static int core_to_pos(int nr)
210 {
211         u32 cores = cpu_pos_mask();
212         int i, count = 0;
213
214         if (nr == 0) {
215                 return 0;
216         } else if (nr >= hweight32(cores)) {
217                 puts("Not a valid core number.\n");
218                 return -1;
219         }
220
221         for (i = 1; i < 32; i++) {
222                 if (is_pos_valid(i)) {
223                         count++;
224                         if (count == nr)
225                                 break;
226                 }
227         }
228
229         if (count != nr)
230                 return -1;
231
232         return i;
233 }
234
235 int cpu_status(u32 nr)
236 {
237         u64 *table = get_spin_tbl_addr();
238         int pos;
239
240         if (nr == 0) {
241                 printf("table base @ 0x%p\n", table);
242         } else {
243                 pos = core_to_pos(nr);
244                 if (pos < 0)
245                         return -1;
246                 table += pos * WORDS_PER_SPIN_TABLE_ENTRY;
247                 printf("table @ 0x%p\n", table);
248                 printf("   addr - 0x%016llx\n",
249                        table[SPIN_TABLE_ELEM_ENTRY_ADDR_IDX]);
250                 printf("   status   - 0x%016llx\n",
251                        table[SPIN_TABLE_ELEM_STATUS_IDX]);
252                 printf("   lpid  - 0x%016llx\n",
253                        table[SPIN_TABLE_ELEM_LPID_IDX]);
254         }
255
256         return 0;
257 }
258
259 int cpu_release(u32 nr, int argc, char *const argv[])
260 {
261         u64 boot_addr;
262         u64 *table = get_spin_tbl_addr();
263         int pos;
264
265         pos = core_to_pos(nr);
266         if (pos <= 0)
267                 return -1;
268
269         table += pos * WORDS_PER_SPIN_TABLE_ENTRY;
270         boot_addr = simple_strtoull(argv[0], NULL, 16);
271         table[SPIN_TABLE_ELEM_ENTRY_ADDR_IDX] = boot_addr;
272         flush_dcache_range((unsigned long)table,
273                            (unsigned long)table + SPIN_TABLE_ELEM_SIZE);
274         asm volatile("dsb st");
275
276         /*
277          * The secondary CPUs polling the spin-table above for a non-zero
278          * value. To save power "wfe" is called. Thus call "sev" here to
279          * wake the CPUs and let them check the spin-table again (see
280          * slave_cpu loop in lowlevel.S)
281          */
282         asm volatile("sev");
283
284         return 0;
285 }