2 * QEMU model of the ZynqMP APU Control.
4 * Copyright (c) 2013-2022 Xilinx Inc
5 * SPDX-License-Identifier: GPL-2.0-or-later
7 * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> and
8 * Edgar E. Iglesias <edgar.iglesias@xilinx.com>
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
14 #include "migration/vmstate.h"
15 #include "hw/qdev-properties.h"
16 #include "hw/sysbus.h"
18 #include "hw/register.h"
20 #include "qemu/bitops.h"
21 #include "qapi/qmp/qerror.h"
23 #include "hw/misc/xlnx-zynqmp-apu-ctrl.h"
25 #ifndef XILINX_ZYNQMP_APU_ERR_DEBUG
26 #define XILINX_ZYNQMP_APU_ERR_DEBUG 0
29 static void update_wfi_out(void *opaque
)
31 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(opaque
);
32 unsigned int i
, wfi_pending
;
34 wfi_pending
= s
->cpu_pwrdwn_req
& s
->cpu_in_wfi
;
35 for (i
= 0; i
< APU_MAX_CPU
; i
++) {
36 qemu_set_irq(s
->wfi_out
[i
], !!(wfi_pending
& (1 << i
)));
40 static void zynqmp_apu_rvbar_post_write(RegisterInfo
*reg
, uint64_t val
)
42 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
45 for (i
= 0; i
< APU_MAX_CPU
; ++i
) {
46 uint64_t rvbar
= s
->regs
[R_RVBARADDR0L
+ 2 * i
] +
47 ((uint64_t)s
->regs
[R_RVBARADDR0H
+ 2 * i
] << 32);
49 object_property_set_int(OBJECT(s
->cpus
[i
]), "rvbar", rvbar
,
55 static void zynqmp_apu_pwrctl_post_write(RegisterInfo
*reg
, uint64_t val
)
57 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
60 for (i
= 0; i
< APU_MAX_CPU
; i
++) {
62 /* Check if CPU's CPUPWRDNREQ has changed. If yes, update GPIOs. */
63 if (new != (s
->cpu_pwrdwn_req
& (1 << i
))) {
64 qemu_set_irq(s
->cpu_power_status
[i
], !!new);
66 s
->cpu_pwrdwn_req
&= ~(1 << i
);
67 s
->cpu_pwrdwn_req
|= new;
72 static void imr_update_irq(XlnxZynqMPAPUCtrl
*s
)
74 bool pending
= s
->regs
[R_ISR
] & ~s
->regs
[R_IMR
];
75 qemu_set_irq(s
->irq_imr
, pending
);
78 static void isr_postw(RegisterInfo
*reg
, uint64_t val64
)
80 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
84 static uint64_t ien_prew(RegisterInfo
*reg
, uint64_t val64
)
86 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
89 s
->regs
[R_IMR
] &= ~val
;
94 static uint64_t ids_prew(RegisterInfo
*reg
, uint64_t val64
)
96 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
99 s
->regs
[R_IMR
] |= val
;
104 static const RegisterAccessInfo zynqmp_apu_regs_info
[] = {
105 #define RVBAR_REGDEF(n) \
106 { .name = "RVBAR CPU " #n " Low", .addr = A_RVBARADDR ## n ## L, \
107 .reset = 0xffff0000ul, \
108 .post_write = zynqmp_apu_rvbar_post_write, \
109 },{ .name = "RVBAR CPU " #n " High", .addr = A_RVBARADDR ## n ## H, \
110 .post_write = zynqmp_apu_rvbar_post_write, \
112 { .name
= "ERR_CTRL", .addr
= A_APU_ERR_CTRL
,
113 },{ .name
= "ISR", .addr
= A_ISR
,
115 .post_write
= isr_postw
,
116 },{ .name
= "IMR", .addr
= A_IMR
,
119 },{ .name
= "IEN", .addr
= A_IEN
,
120 .pre_write
= ien_prew
,
121 },{ .name
= "IDS", .addr
= A_IDS
,
122 .pre_write
= ids_prew
,
123 },{ .name
= "CONFIG_0", .addr
= A_CONFIG_0
,
125 },{ .name
= "CONFIG_1", .addr
= A_CONFIG_1
,
131 { .name
= "ACE_CTRL", .addr
= A_ACE_CTRL
,
133 },{ .name
= "SNOOP_CTRL", .addr
= A_SNOOP_CTRL
,
134 },{ .name
= "PWRCTL", .addr
= A_PWRCTL
,
135 .post_write
= zynqmp_apu_pwrctl_post_write
,
136 },{ .name
= "PWRSTAT", .addr
= A_PWRSTAT
,
141 static void zynqmp_apu_reset_enter(Object
*obj
, ResetType type
)
143 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
146 for (i
= 0; i
< APU_R_MAX
; ++i
) {
147 register_reset(&s
->regs_info
[i
]);
150 s
->cpu_pwrdwn_req
= 0;
154 static void zynqmp_apu_reset_hold(Object
*obj
)
156 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
162 static const MemoryRegionOps zynqmp_apu_ops
= {
163 .read
= register_read_memory
,
164 .write
= register_write_memory
,
165 .endianness
= DEVICE_LITTLE_ENDIAN
,
167 .min_access_size
= 4,
168 .max_access_size
= 4,
172 static void zynqmp_apu_handle_wfi(void *opaque
, int irq
, int level
)
174 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(opaque
);
176 s
->cpu_in_wfi
= deposit32(s
->cpu_in_wfi
, irq
, 1, level
);
180 static void zynqmp_apu_init(Object
*obj
)
182 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
186 register_init_block32(DEVICE(obj
), zynqmp_apu_regs_info
,
187 ARRAY_SIZE(zynqmp_apu_regs_info
),
188 s
->regs_info
, s
->regs
,
190 XILINX_ZYNQMP_APU_ERR_DEBUG
,
192 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->reg_array
->mem
);
193 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &s
->irq_imr
);
195 for (i
= 0; i
< APU_MAX_CPU
; ++i
) {
196 g_autofree gchar
*prop_name
= g_strdup_printf("cpu%d", i
);
197 object_property_add_link(obj
, prop_name
, TYPE_ARM_CPU
,
198 (Object
**)&s
->cpus
[i
],
199 qdev_prop_allow_set_link_before_realize
,
200 OBJ_PROP_LINK_STRONG
);
203 /* wfi_out is used to connect to PMU GPIs. */
204 qdev_init_gpio_out_named(DEVICE(obj
), s
->wfi_out
, "wfi_out", 4);
205 /* CPU_POWER_STATUS is used to connect to INTC redirect. */
206 qdev_init_gpio_out_named(DEVICE(obj
), s
->cpu_power_status
,
207 "CPU_POWER_STATUS", 4);
208 /* wfi_in is used as input from CPUs as wfi request. */
209 qdev_init_gpio_in_named(DEVICE(obj
), zynqmp_apu_handle_wfi
, "wfi_in", 4);
212 static void zynqmp_apu_finalize(Object
*obj
)
214 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
215 register_finalize_block(s
->reg_array
);
218 static const VMStateDescription vmstate_zynqmp_apu
= {
219 .name
= TYPE_XLNX_ZYNQMP_APU_CTRL
,
221 .minimum_version_id
= 1,
222 .fields
= (VMStateField
[]) {
223 VMSTATE_UINT32_ARRAY(regs
, XlnxZynqMPAPUCtrl
, APU_R_MAX
),
224 VMSTATE_END_OF_LIST(),
228 static void zynqmp_apu_class_init(ObjectClass
*klass
, void *data
)
230 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
231 DeviceClass
*dc
= DEVICE_CLASS(klass
);
233 dc
->vmsd
= &vmstate_zynqmp_apu
;
235 rc
->phases
.enter
= zynqmp_apu_reset_enter
;
236 rc
->phases
.hold
= zynqmp_apu_reset_hold
;
239 static const TypeInfo zynqmp_apu_info
= {
240 .name
= TYPE_XLNX_ZYNQMP_APU_CTRL
,
241 .parent
= TYPE_SYS_BUS_DEVICE
,
242 .instance_size
= sizeof(XlnxZynqMPAPUCtrl
),
243 .class_init
= zynqmp_apu_class_init
,
244 .instance_init
= zynqmp_apu_init
,
245 .instance_finalize
= zynqmp_apu_finalize
,
248 static void zynqmp_apu_register_types(void)
250 type_register_static(&zynqmp_apu_info
);
253 type_init(zynqmp_apu_register_types
)