1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 *
6 * Authors:
7 * Anup Patel <anup.patel@wdc.com>
8 */
9
10 #include <libfdt.h>
11 #include <platform_override.h>
12 #include <sbi/riscv_asm.h>
13 #include <sbi/sbi_hartmask.h>
14 #include <sbi/sbi_platform.h>
15 #include <sbi/sbi_string.h>
16 #include <sbi_utils/fdt/fdt_domain.h>
17 #include <sbi_utils/fdt/fdt_fixup.h>
18 #include <sbi_utils/fdt/fdt_helper.h>
19 #include <sbi_utils/irqchip/fdt_irqchip.h>
20 #include <sbi_utils/serial/fdt_serial.h>
21 #include <sbi_utils/timer/fdt_timer.h>
22 #include <sbi_utils/ipi/fdt_ipi.h>
23 #include <sbi_utils/reset/fdt_reset.h>
24
25 int need_pmp_war = FALSE;
26
27 extern const struct platform_override sifive_fu540;
28
29 static const struct platform_override *special_platforms[] = {
30 &sifive_fu540,
31 };
32
33 static const struct platform_override *generic_plat = NULL;
34 static const struct fdt_match *generic_plat_match = NULL;
35
fw_platform_lookup_special(void * fdt,int root_offset)36 static void fw_platform_lookup_special(void *fdt, int root_offset)
37 {
38 int pos, noff;
39 const struct platform_override *plat;
40 const struct fdt_match *match;
41
42 for (pos = 0; pos < array_size(special_platforms); pos++) {
43 plat = special_platforms[pos];
44 if (!plat->match_table)
45 continue;
46
47 noff = fdt_find_match(fdt, -1, plat->match_table, &match);
48 if (noff < 0)
49 continue;
50
51 generic_plat = plat;
52 generic_plat_match = match;
53 break;
54 }
55 }
56
57 extern struct sbi_platform platform;
58 static u32 generic_hart_index2id[SBI_HARTMASK_MAX_BITS] = { 0 };
59
60 /*
61 * The fw_platform_init() function is called very early on the boot HART
62 * OpenSBI reference firmwares so that platform specific code get chance
63 * to update "platform" instance before it is used.
64 *
65 * The arguments passed to fw_platform_init() function are boot time state
66 * of A0 to A4 register. The "arg0" will be boot HART id and "arg1" will
67 * be address of FDT passed by previous booting stage.
68 *
69 * The return value of fw_platform_init() function is the FDT location. If
70 * FDT is unchanged (or FDT is modified in-place) then fw_platform_init()
71 * can always return the original FDT location (i.e. 'arg1') unmodified.
72 */
fw_platform_init(unsigned long arg0,unsigned long arg1,unsigned long arg2,unsigned long arg3,unsigned long arg4)73 unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,
74 unsigned long arg2, unsigned long arg3,
75 unsigned long arg4)
76 {
77 const char *model;
78 void *fdt = (void *)arg1;
79 u32 hartid, hart_count = 0;
80 int rc, root_offset, cpus_offset, cpu_offset, len;
81
82 root_offset = fdt_path_offset(fdt, "/");
83 if (root_offset < 0)
84 goto fail;
85
86 fw_platform_lookup_special(fdt, root_offset);
87
88 model = fdt_getprop(fdt, root_offset, "model", &len);
89 if (model)
90 sbi_strncpy(platform.name, model, sizeof(platform.name));
91
92 if (generic_plat && generic_plat->features)
93 platform.features = generic_plat->features(generic_plat_match);
94
95 cpus_offset = fdt_path_offset(fdt, "/cpus");
96 if (cpus_offset < 0)
97 goto fail;
98
99 fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
100 rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
101 if (rc)
102 continue;
103
104 if (SBI_HARTMASK_MAX_BITS <= hartid)
105 continue;
106
107 generic_hart_index2id[hart_count++] = hartid;
108 }
109
110 platform.hart_count = hart_count;
111
112 /* Return original FDT pointer */
113 return arg1;
114
115 fail:
116 while (1)
117 wfi();
118 }
119
generic_early_init(bool cold_boot)120 static int generic_early_init(bool cold_boot)
121 {
122 int rc;
123
124 if (generic_plat && generic_plat->early_init) {
125 rc = generic_plat->early_init(cold_boot, generic_plat_match);
126 if (rc)
127 return rc;
128 }
129
130 if (!cold_boot)
131 return 0;
132
133 return fdt_reset_init();
134 }
135
generic_final_init(bool cold_boot)136 static int generic_final_init(bool cold_boot)
137 {
138 void *fdt;
139 int rc;
140
141 if (generic_plat && generic_plat->final_init) {
142 rc = generic_plat->final_init(cold_boot, generic_plat_match);
143 if (rc)
144 return rc;
145 }
146
147 if (!cold_boot)
148 return 0;
149
150 fdt = sbi_scratch_thishart_arg1_ptr();
151
152 fdt_cpu_fixup(fdt);
153 fdt_fixups(fdt);
154 fdt_domain_fixup(fdt);
155
156 if (generic_plat && generic_plat->fdt_fixup) {
157 rc = generic_plat->fdt_fixup(fdt, generic_plat_match);
158 if (rc)
159 return rc;
160 }
161
162 return 0;
163 }
164
generic_early_exit(void)165 static void generic_early_exit(void)
166 {
167 if (generic_plat && generic_plat->early_exit)
168 generic_plat->early_exit(generic_plat_match);
169 }
170
generic_final_exit(void)171 static void generic_final_exit(void)
172 {
173 if (generic_plat && generic_plat->final_exit)
174 generic_plat->final_exit(generic_plat_match);
175 }
176
generic_domains_init(void)177 static int generic_domains_init(void)
178 {
179 return fdt_domains_populate(sbi_scratch_thishart_arg1_ptr());
180 }
181
generic_tlbr_flush_limit(void)182 static u64 generic_tlbr_flush_limit(void)
183 {
184 if (generic_plat && generic_plat->tlbr_flush_limit)
185 return generic_plat->tlbr_flush_limit(generic_plat_match);
186 return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT;
187 }
188
generic_system_reset_check(u32 reset_type,u32 reset_reason)189 static int generic_system_reset_check(u32 reset_type, u32 reset_reason)
190 {
191 if (generic_plat && generic_plat->system_reset_check)
192 return generic_plat->system_reset_check(reset_type,
193 reset_reason,
194 generic_plat_match);
195 return fdt_system_reset_check(reset_type, reset_reason);
196 }
197
generic_system_reset(u32 reset_type,u32 reset_reason)198 static void generic_system_reset(u32 reset_type, u32 reset_reason)
199 {
200 if (generic_plat && generic_plat->system_reset) {
201 generic_plat->system_reset(reset_type, reset_reason,
202 generic_plat_match);
203 return;
204 }
205
206 fdt_system_reset(reset_type, reset_reason);
207 }
208
209 const struct sbi_platform_operations platform_ops = {
210 .early_init = generic_early_init,
211 .final_init = generic_final_init,
212 .early_exit = generic_early_exit,
213 .final_exit = generic_final_exit,
214 .domains_init = generic_domains_init,
215 .console_putc = fdt_serial_putc,
216 .console_getc = fdt_serial_getc,
217 .console_init = fdt_serial_init,
218 .irqchip_init = fdt_irqchip_init,
219 .irqchip_exit = fdt_irqchip_exit,
220 .ipi_send = fdt_ipi_send,
221 .ipi_clear = fdt_ipi_clear,
222 .ipi_init = fdt_ipi_init,
223 .ipi_exit = fdt_ipi_exit,
224 .get_tlbr_flush_limit = generic_tlbr_flush_limit,
225 .timer_value = fdt_timer_value,
226 .timer_event_stop = fdt_timer_event_stop,
227 .timer_event_start = fdt_timer_event_start,
228 .timer_init = fdt_timer_init,
229 .timer_exit = fdt_timer_exit,
230 .system_reset_check = generic_system_reset_check,
231 .system_reset = generic_system_reset,
232 };
233
234 struct sbi_platform platform = {
235 .opensbi_version = OPENSBI_VERSION,
236 .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01),
237 .name = "Generic",
238 .features = SBI_PLATFORM_DEFAULT_FEATURES,
239 .hart_count = SBI_HARTMASK_MAX_BITS,
240 .hart_index2id = generic_hart_index2id,
241 .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
242 .platform_ops_addr = (unsigned long)&platform_ops
243 };
244