Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation | |
1da177e4 LT |
4 | */ |
5 | ||
1da177e4 LT |
6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> | |
8 | #include <linux/linkage.h> | |
9 | #include <linux/mm.h> | |
57c8a661 | 10 | #include <linux/memblock.h> |
fcdb27ad | 11 | #include <linux/pm.h> |
1da177e4 LT |
12 | #include <linux/smp.h> |
13 | ||
14 | #include <asm/bootinfo.h> | |
15 | #include <asm/reboot.h> | |
5c93316c | 16 | #include <asm/setup.h> |
1da177e4 | 17 | #include <asm/sibyte/board.h> |
87353d8a | 18 | #include <asm/smp-ops.h> |
1da177e4 | 19 | |
df78b5c8 AJ |
20 | #include <asm/fw/cfe/cfe_api.h> |
21 | #include <asm/fw/cfe/cfe_error.h> | |
1da177e4 LT |
22 | |
23 | /* Max ram addressable in 32-bit segments */ | |
875d43e7 | 24 | #ifdef CONFIG_64BIT |
1da177e4 LT |
25 | #define MAX_RAM_SIZE (~0ULL) |
26 | #else | |
27 | #ifdef CONFIG_HIGHMEM | |
34adb28d | 28 | #ifdef CONFIG_PHYS_ADDR_T_64BIT |
1da177e4 LT |
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 | ||
1da177e4 LT |
38 | int cfe_cons_handle; |
39 | ||
40 | #ifdef CONFIG_BLK_DEV_INITRD | |
41 | extern unsigned long initrd_start, initrd_end; | |
42 | #endif | |
43 | ||
b3f6df9f | 44 | static void __noreturn cfe_linux_exit(void *arg) |
1da177e4 LT |
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; | |
8691e5a8 | 55 | smp_call_function(cfe_linux_exit, arg, 0); |
1da177e4 LT |
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 | ||
b3f6df9f | 65 | static void __noreturn cfe_linux_restart(char *command) |
1da177e4 LT |
66 | { |
67 | static const int zero; | |
68 | ||
69 | cfe_linux_exit((void *)&zero); | |
70 | } | |
71 | ||
b3f6df9f | 72 | static void __noreturn cfe_linux_halt(void) |
1da177e4 LT |
73 | { |
74 | static const int one = 1; | |
75 | ||
76 | cfe_linux_exit((void *)&one); | |
77 | } | |
78 | ||
79 | static __init void prom_meminit(void) | |
80 | { | |
34adb28d | 81 | u64 addr, size, type; /* regardless of PHYS_ADDR_T_64BIT */ |
1da177e4 LT |
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("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))) { | |
e7ae8d17 TB |
111 | memblock_add(addr, |
112 | initrd_pstart - addr); | |
1da177e4 LT |
113 | rd_flag = 1; |
114 | } | |
115 | if ((initrd_pend > addr) && | |
116 | (initrd_pend < (addr + size))) { | |
e7ae8d17 TB |
117 | memblock_add(initrd_pend, |
118 | (addr + size) - initrd_pend); | |
1da177e4 LT |
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; | |
e7ae8d17 | 137 | memblock_add(addr, size); |
1da177e4 | 138 | } |
1da177e4 LT |
139 | } |
140 | } | |
141 | #ifdef CONFIG_BLK_DEV_INITRD | |
142 | if (initrd_start) { | |
e7ae8d17 TB |
143 | memblock_add(initrd_pstart, initrd_pend - initrd_pstart); |
144 | memblock_reserve(initrd_pstart, initrd_pend - initrd_pstart); | |
1da177e4 LT |
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>" | |
70342287 | 168 | * e.g. initrd=3abfd@80010000. This is set up by the loader. |
1da177e4 LT |
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; | |
36a88530 | 191 | printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start); |
1da177e4 LT |
192 | return 1; |
193 | fail: | |
36a88530 | 194 | printk("Bad initrd argument. Disabling initrd\n"); |
1da177e4 LT |
195 | initrd_start = 0; |
196 | initrd_end = 0; | |
197 | return 1; | |
198 | } | |
199 | ||
200 | #endif | |
201 | ||
ff2c8252 MR |
202 | extern const struct plat_smp_ops sb_smp_ops; |
203 | extern const struct plat_smp_ops bcm1480_smp_ops; | |
87353d8a | 204 | |
1da177e4 LT |
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; | |
1da177e4 LT |
215 | |
216 | _machine_restart = cfe_linux_restart; | |
70342287 | 217 | _machine_halt = cfe_linux_halt; |
fcdb27ad | 218 | pm_power_off = cfe_linux_halt; |
1da177e4 LT |
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 */ | |
36a88530 | 251 | printk("CFE's entrypoint seal doesn't match. Spinning."); |
1da177e4 LT |
252 | while (1) ; |
253 | } | |
254 | cfe_init(cfe_handle, cfe_ept); | |
42a3b4f2 | 255 | /* |
1da177e4 LT |
256 | * Get the handle for (at least) prom_putchar, possibly for |
257 | * boot console | |
258 | */ | |
259 | cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); | |
7580c9c3 | 260 | if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, COMMAND_LINE_SIZE) < 0) { |
e082f188 | 261 | if (argc >= 0) { |
1da177e4 LT |
262 | /* The loader should have set the command line */ |
263 | /* too early for panic to do any good */ | |
36a88530 | 264 | printk("LINUX_CMDLINE not defined in cfe."); |
1da177e4 LT |
265 | while (1) ; |
266 | } | |
267 | } | |
268 | ||
1da177e4 LT |
269 | #ifdef CONFIG_BLK_DEV_INITRD |
270 | { | |
271 | char *ptr; | |
70342287 | 272 | /* Need to find out early whether we've got an initrd. So scan |
1da177e4 LT |
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(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. */ | |
7580c9c3 | 291 | arcs_cmdline[COMMAND_LINE_SIZE-1] = 0; |
1da177e4 | 292 | |
1da177e4 | 293 | prom_meminit(); |
87353d8a RB |
294 | |
295 | #if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250) | |
296 | register_smp_ops(&sb_smp_ops); | |
297 | #endif | |
b984d7b5 | 298 | #ifdef CONFIG_SIBYTE_BCM1x80 |
87353d8a RB |
299 | register_smp_ops(&bcm1480_smp_ops); |
300 | #endif | |
1da177e4 LT |
301 | } |
302 | ||
1da177e4 LT |
303 | void prom_putchar(char c) |
304 | { | |
305 | int ret; | |
306 | ||
307 | while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0) | |
308 | ; | |
309 | } |