1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation |
4 | */ |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/linkage.h> |
9 | #include <linux/mm.h> |
10 | #include <linux/memblock.h> |
11 | #include <linux/pm.h> |
12 | #include <linux/smp.h> |
13 | |
14 | #include <asm/bootinfo.h> |
15 | #include <asm/reboot.h> |
16 | #include <asm/setup.h> |
17 | #include <asm/sibyte/board.h> |
18 | #include <asm/smp-ops.h> |
19 | |
20 | #include <asm/fw/cfe/cfe_api.h> |
21 | #include <asm/fw/cfe/cfe_error.h> |
22 | |
23 | /* Max ram addressable in 32-bit segments */ |
24 | #ifdef CONFIG_64BIT |
25 | #define MAX_RAM_SIZE (~0ULL) |
26 | #else |
27 | #ifdef CONFIG_HIGHMEM |
28 | #ifdef CONFIG_PHYS_ADDR_T_64BIT |
29 | #define MAX_RAM_SIZE (~0ULL) |
30 | #else |
31 | #define MAX_RAM_SIZE (0xffffffffULL) |
32 | #endif |
33 | #else |
34 | #define MAX_RAM_SIZE (0x1fffffffULL) |
35 | #endif |
36 | #endif |
37 | |
38 | int cfe_cons_handle; |
39 | |
40 | #ifdef CONFIG_BLK_DEV_INITRD |
41 | extern unsigned long initrd_start, initrd_end; |
42 | #endif |
43 | |
44 | static void __noreturn cfe_linux_exit(void *arg) |
45 | { |
46 | int warm = *(int *)arg; |
47 | |
48 | if (smp_processor_id()) { |
49 | static int reboot_smp; |
50 | |
51 | /* Don't repeat the process from another CPU */ |
52 | if (!reboot_smp) { |
53 | /* Get CPU 0 to do the cfe_exit */ |
54 | reboot_smp = 1; |
55 | smp_call_function(func: cfe_linux_exit, info: arg, wait: 0); |
56 | } |
57 | } else { |
58 | printk("Passing control back to CFE...\n" ); |
59 | cfe_exit(warm, 0); |
60 | printk("cfe_exit returned??\n" ); |
61 | } |
62 | while (1); |
63 | } |
64 | |
65 | static void __noreturn cfe_linux_restart(char *command) |
66 | { |
67 | static const int zero; |
68 | |
69 | cfe_linux_exit(arg: (void *)&zero); |
70 | } |
71 | |
72 | static void __noreturn cfe_linux_halt(void) |
73 | { |
74 | static const int one = 1; |
75 | |
76 | cfe_linux_exit(arg: (void *)&one); |
77 | } |
78 | |
79 | static __init void prom_meminit(void) |
80 | { |
81 | u64 addr, size, type; /* regardless of PHYS_ADDR_T_64BIT */ |
82 | int mem_flags = 0; |
83 | unsigned int idx; |
84 | int rd_flag; |
85 | #ifdef CONFIG_BLK_DEV_INITRD |
86 | unsigned long initrd_pstart; |
87 | unsigned long initrd_pend; |
88 | |
89 | initrd_pstart = CPHYSADDR(initrd_start); |
90 | initrd_pend = CPHYSADDR(initrd_end); |
91 | if (initrd_start && |
92 | ((initrd_pstart > MAX_RAM_SIZE) |
93 | || (initrd_pend > MAX_RAM_SIZE))) { |
94 | panic(fmt: "initrd out of addressable memory" ); |
95 | } |
96 | |
97 | #endif /* INITRD */ |
98 | |
99 | for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE; |
100 | idx++) { |
101 | rd_flag = 0; |
102 | if (type == CFE_MI_AVAILABLE) { |
103 | /* |
104 | * See if this block contains (any portion of) the |
105 | * ramdisk |
106 | */ |
107 | #ifdef CONFIG_BLK_DEV_INITRD |
108 | if (initrd_start) { |
109 | if ((initrd_pstart > addr) && |
110 | (initrd_pstart < (addr + size))) { |
111 | memblock_add(base: addr, |
112 | size: initrd_pstart - addr); |
113 | rd_flag = 1; |
114 | } |
115 | if ((initrd_pend > addr) && |
116 | (initrd_pend < (addr + size))) { |
117 | memblock_add(base: initrd_pend, |
118 | size: (addr + size) - initrd_pend); |
119 | rd_flag = 1; |
120 | } |
121 | } |
122 | #endif |
123 | if (!rd_flag) { |
124 | if (addr > MAX_RAM_SIZE) |
125 | continue; |
126 | if (addr+size > MAX_RAM_SIZE) |
127 | size = MAX_RAM_SIZE - (addr+size) + 1; |
128 | /* |
129 | * memcpy/__copy_user prefetch, which |
130 | * will cause a bus error for |
131 | * KSEG/KUSEG addrs not backed by RAM. |
132 | * Hence, reserve some padding for the |
133 | * prefetch distance. |
134 | */ |
135 | if (size > 512) |
136 | size -= 512; |
137 | memblock_add(base: addr, size); |
138 | } |
139 | } |
140 | } |
141 | #ifdef CONFIG_BLK_DEV_INITRD |
142 | if (initrd_start) { |
143 | memblock_add(base: initrd_pstart, size: initrd_pend - initrd_pstart); |
144 | memblock_reserve(base: initrd_pstart, size: initrd_pend - initrd_pstart); |
145 | } |
146 | #endif |
147 | } |
148 | |
149 | #ifdef CONFIG_BLK_DEV_INITRD |
150 | static int __init initrd_setup(char *str) |
151 | { |
152 | char rdarg[64]; |
153 | int idx; |
154 | char *tmp, *endptr; |
155 | unsigned long initrd_size; |
156 | |
157 | /* Make a copy of the initrd argument so we can smash it up here */ |
158 | for (idx = 0; idx < sizeof(rdarg)-1; idx++) { |
159 | if (!str[idx] || (str[idx] == ' ')) break; |
160 | rdarg[idx] = str[idx]; |
161 | } |
162 | |
163 | rdarg[idx] = 0; |
164 | str = rdarg; |
165 | |
166 | /* |
167 | *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>" |
168 | * e.g. initrd=3abfd@80010000. This is set up by the loader. |
169 | */ |
170 | for (tmp = str; *tmp != '@'; tmp++) { |
171 | if (!*tmp) { |
172 | goto fail; |
173 | } |
174 | } |
175 | *tmp = 0; |
176 | tmp++; |
177 | if (!*tmp) { |
178 | goto fail; |
179 | } |
180 | initrd_size = simple_strtoul(str, &endptr, 16); |
181 | if (*endptr) { |
182 | *(tmp-1) = '@'; |
183 | goto fail; |
184 | } |
185 | *(tmp-1) = '@'; |
186 | initrd_start = simple_strtoul(tmp, &endptr, 16); |
187 | if (*endptr) { |
188 | goto fail; |
189 | } |
190 | initrd_end = initrd_start + initrd_size; |
191 | printk("Found initrd of %lx@%lx\n" , initrd_size, initrd_start); |
192 | return 1; |
193 | fail: |
194 | printk("Bad initrd argument. Disabling initrd\n" ); |
195 | initrd_start = 0; |
196 | initrd_end = 0; |
197 | return 1; |
198 | } |
199 | |
200 | #endif |
201 | |
202 | extern const struct plat_smp_ops sb_smp_ops; |
203 | extern const struct plat_smp_ops bcm1480_smp_ops; |
204 | |
205 | /* |
206 | * prom_init is called just after the cpu type is determined, from setup_arch() |
207 | */ |
208 | void __init prom_init(void) |
209 | { |
210 | uint64_t cfe_ept, cfe_handle; |
211 | unsigned int cfe_eptseal; |
212 | int argc = fw_arg0; |
213 | char **envp = (char **) fw_arg2; |
214 | int *prom_vec = (int *) fw_arg3; |
215 | |
216 | _machine_restart = cfe_linux_restart; |
217 | _machine_halt = cfe_linux_halt; |
218 | pm_power_off = cfe_linux_halt; |
219 | |
220 | /* |
221 | * Check if a loader was used; if NOT, the 4 arguments are |
222 | * what CFE gives us (handle, 0, EPT and EPTSEAL) |
223 | */ |
224 | if (argc < 0) { |
225 | cfe_handle = (uint64_t)(long)argc; |
226 | cfe_ept = (long)envp; |
227 | cfe_eptseal = (uint32_t)(unsigned long)prom_vec; |
228 | } else { |
229 | if ((int32_t)(long)prom_vec < 0) { |
230 | /* |
231 | * Old loader; all it gives us is the handle, |
232 | * so use the "known" entrypoint and assume |
233 | * the seal. |
234 | */ |
235 | cfe_handle = (uint64_t)(long)prom_vec; |
236 | cfe_ept = (uint64_t)((int32_t)0x9fc00500); |
237 | cfe_eptseal = CFE_EPTSEAL; |
238 | } else { |
239 | /* |
240 | * Newer loaders bundle the handle/ept/eptseal |
241 | * Note: prom_vec is in the loader's useg |
242 | * which is still alive in the TLB. |
243 | */ |
244 | cfe_handle = (uint64_t)((int32_t *)prom_vec)[0]; |
245 | cfe_ept = (uint64_t)((int32_t *)prom_vec)[2]; |
246 | cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3]; |
247 | } |
248 | } |
249 | if (cfe_eptseal != CFE_EPTSEAL) { |
250 | /* too early for panic to do any good */ |
251 | printk("CFE's entrypoint seal doesn't match. Spinning." ); |
252 | while (1) ; |
253 | } |
254 | cfe_init(cfe_handle, cfe_ept); |
255 | /* |
256 | * Get the handle for (at least) prom_putchar, possibly for |
257 | * boot console |
258 | */ |
259 | cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); |
260 | if (cfe_getenv("LINUX_CMDLINE" , arcs_cmdline, COMMAND_LINE_SIZE) < 0) { |
261 | if (argc >= 0) { |
262 | /* The loader should have set the command line */ |
263 | /* too early for panic to do any good */ |
264 | printk("LINUX_CMDLINE not defined in cfe." ); |
265 | while (1) ; |
266 | } |
267 | } |
268 | |
269 | #ifdef CONFIG_BLK_DEV_INITRD |
270 | { |
271 | char *ptr; |
272 | /* Need to find out early whether we've got an initrd. So scan |
273 | the list looking now */ |
274 | for (ptr = arcs_cmdline; *ptr; ptr++) { |
275 | while (*ptr == ' ') { |
276 | ptr++; |
277 | } |
278 | if (!strncmp(ptr, "initrd=" , 7)) { |
279 | initrd_setup(str: ptr+7); |
280 | break; |
281 | } else { |
282 | while (*ptr && (*ptr != ' ')) { |
283 | ptr++; |
284 | } |
285 | } |
286 | } |
287 | } |
288 | #endif /* CONFIG_BLK_DEV_INITRD */ |
289 | |
290 | /* Not sure this is needed, but it's the safe way. */ |
291 | arcs_cmdline[COMMAND_LINE_SIZE-1] = 0; |
292 | |
293 | prom_meminit(); |
294 | |
295 | #if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250) |
296 | register_smp_ops(&sb_smp_ops); |
297 | #endif |
298 | #ifdef CONFIG_SIBYTE_BCM1x80 |
299 | register_smp_ops(&bcm1480_smp_ops); |
300 | #endif |
301 | } |
302 | |
303 | void prom_putchar(char c) |
304 | { |
305 | int ret; |
306 | |
307 | while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0) |
308 | ; |
309 | } |
310 | |