1 | /* |
2 | * QEMU RISC-V Board Compatible with SiFive Freedom U SDK |
3 | * |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu |
5 | * Copyright (c) 2017 SiFive, Inc. |
6 | * |
7 | * Provides a board compatible with the SiFive Freedom U SDK: |
8 | * |
9 | * 0) UART |
10 | * 1) CLINT (Core Level Interruptor) |
11 | * 2) PLIC (Platform Level Interrupt Controller) |
12 | * |
13 | * This board currently uses a hardcoded devicetree that indicates one hart. |
14 | * |
15 | * This program is free software; you can redistribute it and/or modify it |
16 | * under the terms and conditions of the GNU General Public License, |
17 | * version 2 or later, as published by the Free Software Foundation. |
18 | * |
19 | * This program is distributed in the hope it will be useful, but WITHOUT |
20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
22 | * more details. |
23 | * |
24 | * You should have received a copy of the GNU General Public License along with |
25 | * this program. If not, see <http://www.gnu.org/licenses/>. |
26 | */ |
27 | |
28 | #include "qemu/osdep.h" |
29 | #include "qemu/log.h" |
30 | #include "qemu/error-report.h" |
31 | #include "qapi/error.h" |
32 | #include "hw/boards.h" |
33 | #include "hw/loader.h" |
34 | #include "hw/sysbus.h" |
35 | #include "hw/char/serial.h" |
36 | #include "target/riscv/cpu.h" |
37 | #include "hw/riscv/riscv_hart.h" |
38 | #include "hw/riscv/sifive_plic.h" |
39 | #include "hw/riscv/sifive_clint.h" |
40 | #include "hw/riscv/sifive_uart.h" |
41 | #include "hw/riscv/sifive_prci.h" |
42 | #include "hw/riscv/sifive_u.h" |
43 | #include "hw/riscv/boot.h" |
44 | #include "chardev/char.h" |
45 | #include "sysemu/arch_init.h" |
46 | #include "sysemu/device_tree.h" |
47 | #include "sysemu/sysemu.h" |
48 | #include "exec/address-spaces.h" |
49 | |
50 | #include <libfdt.h> |
51 | |
52 | #define BIOS_FILENAME "opensbi-riscv64-sifive_u-fw_jump.bin" |
53 | |
54 | static const struct MemmapEntry { |
55 | hwaddr base; |
56 | hwaddr size; |
57 | } sifive_u_memmap[] = { |
58 | [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, |
59 | [SIFIVE_U_MROM] = { 0x1000, 0x11000 }, |
60 | [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, |
61 | [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, |
62 | [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, |
63 | [SIFIVE_U_UART1] = { 0x10023000, 0x1000 }, |
64 | [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, |
65 | [SIFIVE_U_GEM] = { 0x100900FC, 0x2000 }, |
66 | }; |
67 | |
68 | #define GEM_REVISION 0x10070109 |
69 | |
70 | static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, |
71 | uint64_t mem_size, const char *cmdline) |
72 | { |
73 | void *fdt; |
74 | int cpu; |
75 | uint32_t *cells; |
76 | char *nodename; |
77 | char ethclk_names[] = "pclk\0hclk\0tx_clk" ; |
78 | uint32_t plic_phandle, ethclk_phandle, phandle = 1; |
79 | |
80 | fdt = s->fdt = create_device_tree(&s->fdt_size); |
81 | if (!fdt) { |
82 | error_report("create_device_tree() failed" ); |
83 | exit(1); |
84 | } |
85 | |
86 | qemu_fdt_setprop_string(fdt, "/" , "model" , "ucbbar,spike-bare,qemu" ); |
87 | qemu_fdt_setprop_string(fdt, "/" , "compatible" , "ucbbar,spike-bare-dev" ); |
88 | qemu_fdt_setprop_cell(fdt, "/" , "#size-cells" , 0x2); |
89 | qemu_fdt_setprop_cell(fdt, "/" , "#address-cells" , 0x2); |
90 | |
91 | qemu_fdt_add_subnode(fdt, "/soc" ); |
92 | qemu_fdt_setprop(fdt, "/soc" , "ranges" , NULL, 0); |
93 | qemu_fdt_setprop_string(fdt, "/soc" , "compatible" , "simple-bus" ); |
94 | qemu_fdt_setprop_cell(fdt, "/soc" , "#size-cells" , 0x2); |
95 | qemu_fdt_setprop_cell(fdt, "/soc" , "#address-cells" , 0x2); |
96 | |
97 | nodename = g_strdup_printf("/memory@%lx" , |
98 | (long)memmap[SIFIVE_U_DRAM].base); |
99 | qemu_fdt_add_subnode(fdt, nodename); |
100 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , |
101 | memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base, |
102 | mem_size >> 32, mem_size); |
103 | qemu_fdt_setprop_string(fdt, nodename, "device_type" , "memory" ); |
104 | g_free(nodename); |
105 | |
106 | qemu_fdt_add_subnode(fdt, "/cpus" ); |
107 | qemu_fdt_setprop_cell(fdt, "/cpus" , "timebase-frequency" , |
108 | SIFIVE_CLINT_TIMEBASE_FREQ); |
109 | qemu_fdt_setprop_cell(fdt, "/cpus" , "#size-cells" , 0x0); |
110 | qemu_fdt_setprop_cell(fdt, "/cpus" , "#address-cells" , 0x1); |
111 | |
112 | for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) { |
113 | int cpu_phandle = phandle++; |
114 | nodename = g_strdup_printf("/cpus/cpu@%d" , cpu); |
115 | char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller" , cpu); |
116 | char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]); |
117 | qemu_fdt_add_subnode(fdt, nodename); |
118 | qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency" , |
119 | SIFIVE_U_CLOCK_FREQ); |
120 | qemu_fdt_setprop_string(fdt, nodename, "mmu-type" , "riscv,sv48" ); |
121 | qemu_fdt_setprop_string(fdt, nodename, "riscv,isa" , isa); |
122 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "riscv" ); |
123 | qemu_fdt_setprop_string(fdt, nodename, "status" , "okay" ); |
124 | qemu_fdt_setprop_cell(fdt, nodename, "reg" , cpu); |
125 | qemu_fdt_setprop_string(fdt, nodename, "device_type" , "cpu" ); |
126 | qemu_fdt_add_subnode(fdt, intc); |
127 | qemu_fdt_setprop_cell(fdt, intc, "phandle" , cpu_phandle); |
128 | qemu_fdt_setprop_cell(fdt, intc, "linux,phandle" , cpu_phandle); |
129 | qemu_fdt_setprop_string(fdt, intc, "compatible" , "riscv,cpu-intc" ); |
130 | qemu_fdt_setprop(fdt, intc, "interrupt-controller" , NULL, 0); |
131 | qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells" , 1); |
132 | g_free(isa); |
133 | g_free(intc); |
134 | g_free(nodename); |
135 | } |
136 | |
137 | cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); |
138 | for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { |
139 | nodename = |
140 | g_strdup_printf("/cpus/cpu@%d/interrupt-controller" , cpu); |
141 | uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); |
142 | cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); |
143 | cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); |
144 | cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); |
145 | cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); |
146 | g_free(nodename); |
147 | } |
148 | nodename = g_strdup_printf("/soc/clint@%lx" , |
149 | (long)memmap[SIFIVE_U_CLINT].base); |
150 | qemu_fdt_add_subnode(fdt, nodename); |
151 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "riscv,clint0" ); |
152 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , |
153 | 0x0, memmap[SIFIVE_U_CLINT].base, |
154 | 0x0, memmap[SIFIVE_U_CLINT].size); |
155 | qemu_fdt_setprop(fdt, nodename, "interrupts-extended" , |
156 | cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); |
157 | g_free(cells); |
158 | g_free(nodename); |
159 | |
160 | plic_phandle = phandle++; |
161 | cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); |
162 | for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { |
163 | nodename = |
164 | g_strdup_printf("/cpus/cpu@%d/interrupt-controller" , cpu); |
165 | uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); |
166 | cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); |
167 | cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT); |
168 | cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); |
169 | cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); |
170 | g_free(nodename); |
171 | } |
172 | nodename = g_strdup_printf("/soc/interrupt-controller@%lx" , |
173 | (long)memmap[SIFIVE_U_PLIC].base); |
174 | qemu_fdt_add_subnode(fdt, nodename); |
175 | qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells" , 1); |
176 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "riscv,plic0" ); |
177 | qemu_fdt_setprop(fdt, nodename, "interrupt-controller" , NULL, 0); |
178 | qemu_fdt_setprop(fdt, nodename, "interrupts-extended" , |
179 | cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); |
180 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , |
181 | 0x0, memmap[SIFIVE_U_PLIC].base, |
182 | 0x0, memmap[SIFIVE_U_PLIC].size); |
183 | qemu_fdt_setprop_string(fdt, nodename, "reg-names" , "control" ); |
184 | qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority" , 7); |
185 | qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev" , 0x35); |
186 | qemu_fdt_setprop_cells(fdt, nodename, "phandle" , plic_phandle); |
187 | qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle" , plic_phandle); |
188 | plic_phandle = qemu_fdt_get_phandle(fdt, nodename); |
189 | g_free(cells); |
190 | g_free(nodename); |
191 | |
192 | ethclk_phandle = phandle++; |
193 | nodename = g_strdup_printf("/soc/ethclk" ); |
194 | qemu_fdt_add_subnode(fdt, nodename); |
195 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "fixed-clock" ); |
196 | qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells" , 0x0); |
197 | qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency" , |
198 | SIFIVE_U_GEM_CLOCK_FREQ); |
199 | qemu_fdt_setprop_cell(fdt, nodename, "phandle" , ethclk_phandle); |
200 | qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle" , ethclk_phandle); |
201 | ethclk_phandle = qemu_fdt_get_phandle(fdt, nodename); |
202 | g_free(nodename); |
203 | |
204 | nodename = g_strdup_printf("/soc/ethernet@%lx" , |
205 | (long)memmap[SIFIVE_U_GEM].base); |
206 | qemu_fdt_add_subnode(fdt, nodename); |
207 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "cdns,macb" ); |
208 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , |
209 | 0x0, memmap[SIFIVE_U_GEM].base, |
210 | 0x0, memmap[SIFIVE_U_GEM].size); |
211 | qemu_fdt_setprop_string(fdt, nodename, "reg-names" , "control" ); |
212 | qemu_fdt_setprop_string(fdt, nodename, "phy-mode" , "gmii" ); |
213 | qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent" , plic_phandle); |
214 | qemu_fdt_setprop_cells(fdt, nodename, "interrupts" , SIFIVE_U_GEM_IRQ); |
215 | qemu_fdt_setprop_cells(fdt, nodename, "clocks" , |
216 | ethclk_phandle, ethclk_phandle, ethclk_phandle); |
217 | qemu_fdt_setprop(fdt, nodename, "clocks-names" , ethclk_names, |
218 | sizeof(ethclk_names)); |
219 | qemu_fdt_setprop_cells(fdt, nodename, "#address-cells" , 1); |
220 | qemu_fdt_setprop_cells(fdt, nodename, "#size-cells" , 0); |
221 | g_free(nodename); |
222 | |
223 | nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0" , |
224 | (long)memmap[SIFIVE_U_GEM].base); |
225 | qemu_fdt_add_subnode(fdt, nodename); |
226 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , 0x0); |
227 | g_free(nodename); |
228 | |
229 | nodename = g_strdup_printf("/soc/uart@%lx" , |
230 | (long)memmap[SIFIVE_U_UART0].base); |
231 | qemu_fdt_add_subnode(fdt, nodename); |
232 | qemu_fdt_setprop_string(fdt, nodename, "compatible" , "sifive,uart0" ); |
233 | qemu_fdt_setprop_cells(fdt, nodename, "reg" , |
234 | 0x0, memmap[SIFIVE_U_UART0].base, |
235 | 0x0, memmap[SIFIVE_U_UART0].size); |
236 | qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency" , |
237 | SIFIVE_U_CLOCK_FREQ / 2); |
238 | qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent" , plic_phandle); |
239 | qemu_fdt_setprop_cells(fdt, nodename, "interrupts" , SIFIVE_U_UART0_IRQ); |
240 | |
241 | qemu_fdt_add_subnode(fdt, "/chosen" ); |
242 | qemu_fdt_setprop_string(fdt, "/chosen" , "stdout-path" , nodename); |
243 | if (cmdline) { |
244 | qemu_fdt_setprop_string(fdt, "/chosen" , "bootargs" , cmdline); |
245 | } |
246 | g_free(nodename); |
247 | } |
248 | |
249 | static void riscv_sifive_u_init(MachineState *machine) |
250 | { |
251 | const struct MemmapEntry *memmap = sifive_u_memmap; |
252 | |
253 | SiFiveUState *s = g_new0(SiFiveUState, 1); |
254 | MemoryRegion *system_memory = get_system_memory(); |
255 | MemoryRegion *main_mem = g_new(MemoryRegion, 1); |
256 | int i; |
257 | |
258 | /* Initialize SoC */ |
259 | object_initialize_child(OBJECT(machine), "soc" , &s->soc, |
260 | sizeof(s->soc), TYPE_RISCV_U_SOC, |
261 | &error_abort, NULL); |
262 | object_property_set_bool(OBJECT(&s->soc), true, "realized" , |
263 | &error_abort); |
264 | |
265 | /* register RAM */ |
266 | memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram" , |
267 | machine->ram_size, &error_fatal); |
268 | memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DRAM].base, |
269 | main_mem); |
270 | |
271 | /* create device tree */ |
272 | create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); |
273 | |
274 | riscv_find_and_load_firmware(machine, BIOS_FILENAME, |
275 | memmap[SIFIVE_U_DRAM].base); |
276 | |
277 | if (machine->kernel_filename) { |
278 | riscv_load_kernel(machine->kernel_filename); |
279 | } |
280 | |
281 | /* reset vector */ |
282 | uint32_t reset_vec[8] = { |
283 | 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ |
284 | 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ |
285 | 0xf1402573, /* csrr a0, mhartid */ |
286 | #if defined(TARGET_RISCV32) |
287 | 0x0182a283, /* lw t0, 24(t0) */ |
288 | #elif defined(TARGET_RISCV64) |
289 | 0x0182b283, /* ld t0, 24(t0) */ |
290 | #endif |
291 | 0x00028067, /* jr t0 */ |
292 | 0x00000000, |
293 | memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */ |
294 | 0x00000000, |
295 | /* dtb: */ |
296 | }; |
297 | |
298 | /* copy in the reset vector in little_endian byte order */ |
299 | for (i = 0; i < sizeof(reset_vec) >> 2; i++) { |
300 | reset_vec[i] = cpu_to_le32(reset_vec[i]); |
301 | } |
302 | rom_add_blob_fixed_as("mrom.reset" , reset_vec, sizeof(reset_vec), |
303 | memmap[SIFIVE_U_MROM].base, &address_space_memory); |
304 | |
305 | /* copy in the device tree */ |
306 | if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > |
307 | memmap[SIFIVE_U_MROM].size - sizeof(reset_vec)) { |
308 | error_report("not enough space to store device-tree" ); |
309 | exit(1); |
310 | } |
311 | qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); |
312 | rom_add_blob_fixed_as("mrom.fdt" , s->fdt, fdt_totalsize(s->fdt), |
313 | memmap[SIFIVE_U_MROM].base + sizeof(reset_vec), |
314 | &address_space_memory); |
315 | } |
316 | |
317 | static void riscv_sifive_u_soc_init(Object *obj) |
318 | { |
319 | MachineState *ms = MACHINE(qdev_get_machine()); |
320 | SiFiveUSoCState *s = RISCV_U_SOC(obj); |
321 | |
322 | object_initialize_child(obj, "cpus" , &s->cpus, sizeof(s->cpus), |
323 | TYPE_RISCV_HART_ARRAY, &error_abort, NULL); |
324 | object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type" , |
325 | &error_abort); |
326 | object_property_set_int(OBJECT(&s->cpus), ms->smp.cpus, "num-harts" , |
327 | &error_abort); |
328 | |
329 | sysbus_init_child_obj(obj, "gem" , &s->gem, sizeof(s->gem), |
330 | TYPE_CADENCE_GEM); |
331 | } |
332 | |
333 | static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) |
334 | { |
335 | MachineState *ms = MACHINE(qdev_get_machine()); |
336 | SiFiveUSoCState *s = RISCV_U_SOC(dev); |
337 | const struct MemmapEntry *memmap = sifive_u_memmap; |
338 | MemoryRegion *system_memory = get_system_memory(); |
339 | MemoryRegion *mask_rom = g_new(MemoryRegion, 1); |
340 | qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; |
341 | char *plic_hart_config; |
342 | size_t plic_hart_config_len; |
343 | int i; |
344 | Error *err = NULL; |
345 | NICInfo *nd = &nd_table[0]; |
346 | |
347 | object_property_set_bool(OBJECT(&s->cpus), true, "realized" , |
348 | &error_abort); |
349 | |
350 | /* boot rom */ |
351 | memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom" , |
352 | memmap[SIFIVE_U_MROM].size, &error_fatal); |
353 | memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, |
354 | mask_rom); |
355 | |
356 | /* create PLIC hart topology configuration string */ |
357 | plic_hart_config_len = (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1) * |
358 | ms->smp.cpus; |
359 | plic_hart_config = g_malloc0(plic_hart_config_len); |
360 | for (i = 0; i < ms->smp.cpus; i++) { |
361 | if (i != 0) { |
362 | strncat(plic_hart_config, "," , plic_hart_config_len); |
363 | } |
364 | strncat(plic_hart_config, SIFIVE_U_PLIC_HART_CONFIG, |
365 | plic_hart_config_len); |
366 | plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1); |
367 | } |
368 | |
369 | /* MMIO */ |
370 | s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, |
371 | plic_hart_config, |
372 | SIFIVE_U_PLIC_NUM_SOURCES, |
373 | SIFIVE_U_PLIC_NUM_PRIORITIES, |
374 | SIFIVE_U_PLIC_PRIORITY_BASE, |
375 | SIFIVE_U_PLIC_PENDING_BASE, |
376 | SIFIVE_U_PLIC_ENABLE_BASE, |
377 | SIFIVE_U_PLIC_ENABLE_STRIDE, |
378 | SIFIVE_U_PLIC_CONTEXT_BASE, |
379 | SIFIVE_U_PLIC_CONTEXT_STRIDE, |
380 | memmap[SIFIVE_U_PLIC].size); |
381 | sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, |
382 | serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); |
383 | sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, |
384 | serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); |
385 | sifive_clint_create(memmap[SIFIVE_U_CLINT].base, |
386 | memmap[SIFIVE_U_CLINT].size, ms->smp.cpus, |
387 | SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); |
388 | |
389 | for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) { |
390 | plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i); |
391 | } |
392 | |
393 | if (nd->used) { |
394 | qemu_check_nic_model(nd, TYPE_CADENCE_GEM); |
395 | qdev_set_nic_properties(DEVICE(&s->gem), nd); |
396 | } |
397 | object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision" , |
398 | &error_abort); |
399 | object_property_set_bool(OBJECT(&s->gem), true, "realized" , &err); |
400 | if (err) { |
401 | error_propagate(errp, err); |
402 | return; |
403 | } |
404 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); |
405 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, |
406 | plic_gpios[SIFIVE_U_GEM_IRQ]); |
407 | } |
408 | |
409 | static void riscv_sifive_u_machine_init(MachineClass *mc) |
410 | { |
411 | mc->desc = "RISC-V Board compatible with SiFive U SDK" ; |
412 | mc->init = riscv_sifive_u_init; |
413 | /* The real hardware has 5 CPUs, but one of them is a small embedded power |
414 | * management CPU. |
415 | */ |
416 | mc->max_cpus = 4; |
417 | } |
418 | |
419 | DEFINE_MACHINE("sifive_u" , riscv_sifive_u_machine_init) |
420 | |
421 | static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data) |
422 | { |
423 | DeviceClass *dc = DEVICE_CLASS(oc); |
424 | |
425 | dc->realize = riscv_sifive_u_soc_realize; |
426 | /* Reason: Uses serial_hds in realize function, thus can't be used twice */ |
427 | dc->user_creatable = false; |
428 | } |
429 | |
430 | static const TypeInfo riscv_sifive_u_soc_type_info = { |
431 | .name = TYPE_RISCV_U_SOC, |
432 | .parent = TYPE_DEVICE, |
433 | .instance_size = sizeof(SiFiveUSoCState), |
434 | .instance_init = riscv_sifive_u_soc_init, |
435 | .class_init = riscv_sifive_u_soc_class_init, |
436 | }; |
437 | |
438 | static void riscv_sifive_u_soc_register_types(void) |
439 | { |
440 | type_register_static(&riscv_sifive_u_soc_type_info); |
441 | } |
442 | |
443 | type_init(riscv_sifive_u_soc_register_types) |
444 | |