From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement FDT compatibility, so that timer can initilize
and wire irqs based on device tree information.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
target/arm/cpu.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 6e1cbf3d61..96696ed4f1 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -53,6 +53,8 @@
#include "target/arm/gtimer.h"
#include "trace.h"
+#include "hw/core/fdt_generic_util.h"
+
static void arm_cpu_set_pc(CPUState *cs, vaddr value)
{
@@ -2446,3 +2448,116 @@ static void arm_cpu_register_types(void)
}
type_init(arm_cpu_register_types)
+
+#ifndef CONFIG_USER_ONLY
+
+static Object *fdt_armv8_timer_get_intc(FDTMachineInfo *fdti, char *node_path)
+{
+ char intc_node_path[DT_PATH_LENGTH];
+ uint32_t intc_phandle;
+ Error *errp = NULL;
+ DeviceState *intc;
+
+ intc_phandle = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+ "interrupt-parent",
+ 0, true, &errp);
+
+ /* There must be an interrupt-parent */
+ if (errp ||
+ qemu_devtree_get_node_by_phandle(fdti->fdt,
+ intc_node_path, intc_phandle)) {
+ g_assert_not_reached();
+ }
+
+ while (!fdt_init_has_opaque(fdti, intc_node_path)) {
+ fdt_init_yield(fdti);
+ }
+
+ intc = DEVICE(fdt_init_get_opaque(fdti, intc_node_path));
+
+ while (!intc->realized) {
+ fdt_init_yield(fdti);
+ }
+
+ return OBJECT(intc);
+}
+
+static int armv8_timer_fdt_init(char *node_path, FDTMachineInfo *fdti,
+ void *priv)
+{
+ Object *intc = fdt_armv8_timer_get_intc(fdti, node_path);
+ CPUState *cpu;
+ bool map_mode = false;
+ qemu_irq *sec_irqs = NULL;
+ qemu_irq *ns_irqs;
+ qemu_irq *v_irqs;
+ qemu_irq *h_irqs;
+ uint32_t first_cpu_idx;
+ uint32_t num_cpu;
+ bool has_sec_ext;
+ Error *err = NULL;
+
+ first_cpu_idx = object_property_get_uint(intc, "first-cpu-idx", &err);
+ if (!err) {
+ num_cpu = object_property_get_uint(intc, "num-cpu", &err);
+ assert(!err);
+ has_sec_ext = object_property_get_bool(intc, "has-security-extensions",
+ &err);
+ assert(!err);
+ } else {
+ /*
+ * Connect all CPUs with the ARM_FEATURE_GENERIC_TIMER set for
+ * backwards compatibility when the 'first-cpu-idx' property does not
+ * exist.
+ */
+ num_cpu = 0;
+ has_sec_ext = true;
+ }
+
+ if (has_sec_ext) {
+ sec_irqs = fdt_get_irq(fdti, node_path, 0, &map_mode);
+ ns_irqs = fdt_get_irq(fdti, node_path, 1, &map_mode);
+ v_irqs = fdt_get_irq(fdti, node_path, 2, &map_mode);
+ h_irqs = fdt_get_irq(fdti, node_path, 3, &map_mode);
+ } else {
+ ns_irqs = fdt_get_irq(fdti, node_path, 0, &map_mode);
+ v_irqs = fdt_get_irq(fdti, node_path, 1, &map_mode);
+ h_irqs = fdt_get_irq(fdti, node_path, 2, &map_mode);
+ }
+
+ assert(!map_mode); /* not supported for PPI */
+
+ for (cpu = first_cpu; cpu; cpu = CPU_NEXT(cpu)) {
+ ARMCPU *acpu = ARM_CPU(cpu);
+ bool is_gic_cpu;
+
+ if (!arm_feature(&acpu->env, ARM_FEATURE_GENERIC_TIMER)) {
+ continue;
+ }
+
+ is_gic_cpu = cpu->cpu_index >= first_cpu_idx &&
+ cpu->cpu_index < (first_cpu_idx + num_cpu);
+
+ if (!num_cpu || is_gic_cpu) {
+
+ assert(*ns_irqs);
+ assert(*v_irqs);
+ assert(*h_irqs);
+ qdev_connect_gpio_out(DEVICE(acpu), 0, *ns_irqs++);
+ qdev_connect_gpio_out(DEVICE(acpu), 1, *v_irqs++);
+ qdev_connect_gpio_out(DEVICE(acpu), 2, *h_irqs++);
+
+ if (has_sec_ext) {
+ assert(*sec_irqs);
+ qdev_connect_gpio_out(DEVICE(acpu), 3, *sec_irqs++);
+ }
+ }
+ }
+
+ return 0;
+}
+
+fdt_register_compatibility(armv8_timer_fdt_init,
+ "compatible:arm,armv8-timer");
+
+#endif
--
2.43.0