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