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"
22 #include "hw/misc/xlnx-zynqmp-apu-ctrl.h"
24 #ifndef XILINX_ZYNQMP_APU_ERR_DEBUG
25 #define XILINX_ZYNQMP_APU_ERR_DEBUG 0
28 static void update_wfi_out(void *opaque
)
30 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(opaque
);
31 unsigned int i
, wfi_pending
;
33 wfi_pending
= s
->cpu_pwrdwn_req
& s
->cpu_in_wfi
;
34 for (i
= 0; i
< APU_MAX_CPU
; i
++) {
35 qemu_set_irq(s
->wfi_out
[i
], !!(wfi_pending
& (1 << i
)));
39 static void zynqmp_apu_rvbar_post_write(RegisterInfo
*reg
, uint64_t val
)
41 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
44 for (i
= 0; i
< APU_MAX_CPU
; ++i
) {
45 uint64_t rvbar
= s
->regs
[R_RVBARADDR0L
+ 2 * i
] +
46 ((uint64_t)s
->regs
[R_RVBARADDR0H
+ 2 * i
] << 32);
48 object_property_set_int(OBJECT(s
->cpus
[i
]), "rvbar", rvbar
,
54 static void zynqmp_apu_pwrctl_post_write(RegisterInfo
*reg
, uint64_t val
)
56 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
59 for (i
= 0; i
< APU_MAX_CPU
; i
++) {
61 /* Check if CPU's CPUPWRDNREQ has changed. If yes, update GPIOs. */
62 if (new != (s
->cpu_pwrdwn_req
& (1 << i
))) {
63 qemu_set_irq(s
->cpu_power_status
[i
], !!new);
65 s
->cpu_pwrdwn_req
&= ~(1 << i
);
66 s
->cpu_pwrdwn_req
|= new;
71 static void imr_update_irq(XlnxZynqMPAPUCtrl
*s
)
73 bool pending
= s
->regs
[R_ISR
] & ~s
->regs
[R_IMR
];
74 qemu_set_irq(s
->irq_imr
, pending
);
77 static void isr_postw(RegisterInfo
*reg
, uint64_t val64
)
79 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
83 static uint64_t ien_prew(RegisterInfo
*reg
, uint64_t val64
)
85 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
88 s
->regs
[R_IMR
] &= ~val
;
93 static uint64_t ids_prew(RegisterInfo
*reg
, uint64_t val64
)
95 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(reg
->opaque
);
98 s
->regs
[R_IMR
] |= val
;
103 static const RegisterAccessInfo zynqmp_apu_regs_info
[] = {
104 #define RVBAR_REGDEF(n) \
105 { .name = "RVBAR CPU " #n " Low", .addr = A_RVBARADDR ## n ## L, \
106 .reset = 0xffff0000ul, \
107 .post_write = zynqmp_apu_rvbar_post_write, \
108 },{ .name = "RVBAR CPU " #n " High", .addr = A_RVBARADDR ## n ## H, \
109 .post_write = zynqmp_apu_rvbar_post_write, \
111 { .name
= "ERR_CTRL", .addr
= A_APU_ERR_CTRL
,
112 },{ .name
= "ISR", .addr
= A_ISR
,
114 .post_write
= isr_postw
,
115 },{ .name
= "IMR", .addr
= A_IMR
,
118 },{ .name
= "IEN", .addr
= A_IEN
,
119 .pre_write
= ien_prew
,
120 },{ .name
= "IDS", .addr
= A_IDS
,
121 .pre_write
= ids_prew
,
122 },{ .name
= "CONFIG_0", .addr
= A_CONFIG_0
,
124 },{ .name
= "CONFIG_1", .addr
= A_CONFIG_1
,
130 { .name
= "ACE_CTRL", .addr
= A_ACE_CTRL
,
132 },{ .name
= "SNOOP_CTRL", .addr
= A_SNOOP_CTRL
,
133 },{ .name
= "PWRCTL", .addr
= A_PWRCTL
,
134 .post_write
= zynqmp_apu_pwrctl_post_write
,
135 },{ .name
= "PWRSTAT", .addr
= A_PWRSTAT
,
140 static void zynqmp_apu_reset_enter(Object
*obj
, ResetType type
)
142 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
145 for (i
= 0; i
< APU_R_MAX
; ++i
) {
146 register_reset(&s
->regs_info
[i
]);
149 s
->cpu_pwrdwn_req
= 0;
153 static void zynqmp_apu_reset_hold(Object
*obj
)
155 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
161 static const MemoryRegionOps zynqmp_apu_ops
= {
162 .read
= register_read_memory
,
163 .write
= register_write_memory
,
164 .endianness
= DEVICE_LITTLE_ENDIAN
,
166 .min_access_size
= 4,
167 .max_access_size
= 4,
171 static void zynqmp_apu_handle_wfi(void *opaque
, int irq
, int level
)
173 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(opaque
);
175 s
->cpu_in_wfi
= deposit32(s
->cpu_in_wfi
, irq
, 1, level
);
179 static void zynqmp_apu_init(Object
*obj
)
181 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
185 register_init_block32(DEVICE(obj
), zynqmp_apu_regs_info
,
186 ARRAY_SIZE(zynqmp_apu_regs_info
),
187 s
->regs_info
, s
->regs
,
189 XILINX_ZYNQMP_APU_ERR_DEBUG
,
191 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->reg_array
->mem
);
192 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &s
->irq_imr
);
194 for (i
= 0; i
< APU_MAX_CPU
; ++i
) {
195 g_autofree gchar
*prop_name
= g_strdup_printf("cpu%d", i
);
196 object_property_add_link(obj
, prop_name
, TYPE_ARM_CPU
,
197 (Object
**)&s
->cpus
[i
],
198 qdev_prop_allow_set_link_before_realize
,
199 OBJ_PROP_LINK_STRONG
);
202 /* wfi_out is used to connect to PMU GPIs. */
203 qdev_init_gpio_out_named(DEVICE(obj
), s
->wfi_out
, "wfi_out", 4);
204 /* CPU_POWER_STATUS is used to connect to INTC redirect. */
205 qdev_init_gpio_out_named(DEVICE(obj
), s
->cpu_power_status
,
206 "CPU_POWER_STATUS", 4);
207 /* wfi_in is used as input from CPUs as wfi request. */
208 qdev_init_gpio_in_named(DEVICE(obj
), zynqmp_apu_handle_wfi
, "wfi_in", 4);
211 static void zynqmp_apu_finalize(Object
*obj
)
213 XlnxZynqMPAPUCtrl
*s
= XLNX_ZYNQMP_APU_CTRL(obj
);
214 register_finalize_block(s
->reg_array
);
217 static const VMStateDescription vmstate_zynqmp_apu
= {
218 .name
= TYPE_XLNX_ZYNQMP_APU_CTRL
,
220 .minimum_version_id
= 1,
221 .fields
= (VMStateField
[]) {
222 VMSTATE_UINT32_ARRAY(regs
, XlnxZynqMPAPUCtrl
, APU_R_MAX
),
223 VMSTATE_END_OF_LIST(),
227 static void zynqmp_apu_class_init(ObjectClass
*klass
, void *data
)
229 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
230 DeviceClass
*dc
= DEVICE_CLASS(klass
);
232 dc
->vmsd
= &vmstate_zynqmp_apu
;
234 rc
->phases
.enter
= zynqmp_apu_reset_enter
;
235 rc
->phases
.hold
= zynqmp_apu_reset_hold
;
238 static const TypeInfo zynqmp_apu_info
= {
239 .name
= TYPE_XLNX_ZYNQMP_APU_CTRL
,
240 .parent
= TYPE_SYS_BUS_DEVICE
,
241 .instance_size
= sizeof(XlnxZynqMPAPUCtrl
),
242 .class_init
= zynqmp_apu_class_init
,
243 .instance_init
= zynqmp_apu_init
,
244 .instance_finalize
= zynqmp_apu_finalize
,
247 static void zynqmp_apu_register_types(void)
249 type_register_static(&zynqmp_apu_info
);
252 type_init(zynqmp_apu_register_types
)