1/*
2 * Copyright 2019-2021 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *   Alexander von Gluck IV <kallisti5@unixzen.com>
7 */
8
9#include <arch_cpu_defs.h>
10#include <arch_dtb.h>
11#include <arch_smp.h>
12#include <boot/platform.h>
13#include <boot/stage2.h>
14
15extern "C" {
16#include <libfdt.h>
17}
18
19#include "dtb.h"
20
21
22uint32 gBootHart = 0;
23static uint64 sTimerFrequency = 10000000;
24
25static addr_range sPlic = {0};
26static addr_range sClint = {0};
27
28
29void
30arch_handle_fdt(const void* fdt, int node)
31{
32	const char* deviceType = (const char*)fdt_getprop(fdt, node,
33		"device_type", NULL);
34
35	const char* name = fdt_get_name(fdt, node, NULL);
36	if (strcmp(name, "chosen") == 0) {
37		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "boot-hartid", NULL))
38			gBootHart = fdt32_to_cpu(*prop);
39	} else if (strcmp(name, "cpus") == 0) {
40		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
41			sTimerFrequency = fdt32_to_cpu(*prop);
42	}
43
44	if (deviceType != NULL) {
45		if (strcmp(deviceType, "cpu") == 0) {
46			// TODO: improve incompatible CPU detection
47			if (!(fdt_getprop(fdt, node, "mmu-type", NULL) != NULL))
48				return;
49
50			platform_cpu_info* info;
51			arch_smp_register_cpu(&info);
52			if (info == NULL)
53				return;
54			info->id = fdt32_to_cpu(*(uint32*)fdt_getprop(fdt, node,
55				"reg", NULL));
56			dprintf("cpu\n");
57			dprintf("  id: %" B_PRIu32 "\n", info->id);
58
59			int subNode = fdt_subnode_offset(fdt, node, "interrupt-controller");
60			if (subNode < 0) {
61				dprintf("  [!] no interrupt controller\n");
62			} else {
63				info->phandle = fdt_get_phandle(fdt, subNode);
64				dprintf("  phandle: %" B_PRIu32 "\n", info->phandle);
65			}
66		}
67	}
68	int compatibleLen;
69	const char* compatible = (const char*)fdt_getprop(fdt, node,
70		"compatible", &compatibleLen);
71
72	if (compatible == NULL)
73		return;
74
75	if (dtb_has_fdt_string(compatible, compatibleLen, "riscv,clint0")) {
76		dtb_get_reg(fdt, node, 0, sClint);
77		return;
78	}
79
80	if (dtb_has_fdt_string(compatible, compatibleLen, "riscv,plic0")
81		|| dtb_has_fdt_string(compatible, compatibleLen, "sifive,plic-1.0.0")) {
82		dtb_get_reg(fdt, node, 0, sPlic);
83		int propSize;
84		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
85			dprintf("PLIC contexts\n");
86			uint32 contextId = 0;
87			for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
88				uint32 phandle = fdt32_to_cpu(*it);
89				uint32 interrupt = fdt32_to_cpu(*(it + 1));
90				if (interrupt == sExternInt) {
91					platform_cpu_info* cpuInfo = arch_smp_find_cpu(phandle);
92					dprintf("  context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
93					if (cpuInfo != NULL) {
94						cpuInfo->plicContext = contextId;
95						dprintf("    cpu id: %" B_PRIu32 "\n", cpuInfo->id);
96					}
97				}
98				contextId++;
99			}
100		}
101		return;
102	}
103}
104
105
106void
107arch_dtb_set_kernel_args(void)
108{
109	dprintf("bootHart: %" B_PRIu32 "\n", gBootHart);
110	dprintf("timerFrequency: %" B_PRIu64 "\n", sTimerFrequency);
111	gKernelArgs.arch_args.timerFrequency = sTimerFrequency;
112
113//	gKernelArgs.arch_args.htif  = {.start = 0x40008000, .size = 0x10};
114	gKernelArgs.arch_args.htif  = {.start = 0, .size = 0};
115	gKernelArgs.arch_args.plic  = sPlic;
116	gKernelArgs.arch_args.clint = sClint;
117}
118