xref: /openbmc/qemu/hw/misc/xlnx-zynqmp-apu-ctrl.c (revision ad80e367)
1 /*
2  * QEMU model of the ZynqMP APU Control.
3  *
4  * Copyright (c) 2013-2022 Xilinx Inc
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  *
7  * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> and
8  * Edgar E. Iglesias <edgar.iglesias@xilinx.com>
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
13 #include "qemu/log.h"
14 #include "migration/vmstate.h"
15 #include "hw/qdev-properties.h"
16 #include "hw/sysbus.h"
17 #include "hw/irq.h"
18 #include "hw/register.h"
19 
20 #include "qemu/bitops.h"
21 
22 #include "hw/misc/xlnx-zynqmp-apu-ctrl.h"
23 
24 #ifndef XILINX_ZYNQMP_APU_ERR_DEBUG
25 #define XILINX_ZYNQMP_APU_ERR_DEBUG 0
26 #endif
27 
update_wfi_out(void * opaque)28 static void update_wfi_out(void *opaque)
29 {
30     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque);
31     unsigned int i, wfi_pending;
32 
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)));
36     }
37 }
38 
zynqmp_apu_rvbar_post_write(RegisterInfo * reg,uint64_t val)39 static void zynqmp_apu_rvbar_post_write(RegisterInfo *reg, uint64_t val)
40 {
41     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
42     int i;
43 
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);
47         if (s->cpus[i]) {
48             object_property_set_int(OBJECT(s->cpus[i]), "rvbar", rvbar,
49                                     &error_abort);
50         }
51     }
52 }
53 
zynqmp_apu_pwrctl_post_write(RegisterInfo * reg,uint64_t val)54 static void zynqmp_apu_pwrctl_post_write(RegisterInfo *reg, uint64_t val)
55 {
56     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
57     unsigned int i, new;
58 
59     for (i = 0; i < APU_MAX_CPU; i++) {
60         new = val & (1 << 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);
64         }
65         s->cpu_pwrdwn_req &= ~(1 << i);
66         s->cpu_pwrdwn_req |= new;
67     }
68     update_wfi_out(s);
69 }
70 
imr_update_irq(XlnxZynqMPAPUCtrl * s)71 static void imr_update_irq(XlnxZynqMPAPUCtrl *s)
72 {
73     bool pending = s->regs[R_ISR] & ~s->regs[R_IMR];
74     qemu_set_irq(s->irq_imr, pending);
75 }
76 
isr_postw(RegisterInfo * reg,uint64_t val64)77 static void isr_postw(RegisterInfo *reg, uint64_t val64)
78 {
79     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
80     imr_update_irq(s);
81 }
82 
ien_prew(RegisterInfo * reg,uint64_t val64)83 static uint64_t ien_prew(RegisterInfo *reg, uint64_t val64)
84 {
85     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
86     uint32_t val = val64;
87 
88     s->regs[R_IMR] &= ~val;
89     imr_update_irq(s);
90     return 0;
91 }
92 
ids_prew(RegisterInfo * reg,uint64_t val64)93 static uint64_t ids_prew(RegisterInfo *reg, uint64_t val64)
94 {
95     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
96     uint32_t val = val64;
97 
98     s->regs[R_IMR] |= val;
99     imr_update_irq(s);
100     return 0;
101 }
102 
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,                     \
110     }
111     {   .name = "ERR_CTRL",  .addr = A_APU_ERR_CTRL,
112     },{ .name = "ISR",  .addr = A_ISR,
113         .w1c = 0x1,
114         .post_write = isr_postw,
115     },{ .name = "IMR",  .addr = A_IMR,
116         .reset = 0x1,
117         .ro = 0x1,
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,
123         .reset = 0xf0f,
124     },{ .name = "CONFIG_1",  .addr = A_CONFIG_1,
125     },
126     RVBAR_REGDEF(0),
127     RVBAR_REGDEF(1),
128     RVBAR_REGDEF(2),
129     RVBAR_REGDEF(3),
130     { .name = "ACE_CTRL",  .addr = A_ACE_CTRL,
131         .reset = 0xf000f,
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,
136         .ro = 0x3000f,
137     }
138 };
139 
zynqmp_apu_reset_enter(Object * obj,ResetType type)140 static void zynqmp_apu_reset_enter(Object *obj, ResetType type)
141 {
142     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
143     int i;
144 
145     for (i = 0; i < APU_R_MAX; ++i) {
146         register_reset(&s->regs_info[i]);
147     }
148 
149     s->cpu_pwrdwn_req = 0;
150     s->cpu_in_wfi = 0;
151 }
152 
zynqmp_apu_reset_hold(Object * obj,ResetType type)153 static void zynqmp_apu_reset_hold(Object *obj, ResetType type)
154 {
155     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
156 
157     update_wfi_out(s);
158     imr_update_irq(s);
159 }
160 
161 static const MemoryRegionOps zynqmp_apu_ops = {
162     .read = register_read_memory,
163     .write = register_write_memory,
164     .endianness = DEVICE_LITTLE_ENDIAN,
165     .valid = {
166         .min_access_size = 4,
167         .max_access_size = 4,
168     }
169 };
170 
zynqmp_apu_handle_wfi(void * opaque,int irq,int level)171 static void zynqmp_apu_handle_wfi(void *opaque, int irq, int level)
172 {
173     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque);
174 
175     s->cpu_in_wfi = deposit32(s->cpu_in_wfi, irq, 1, level);
176     update_wfi_out(s);
177 }
178 
zynqmp_apu_init(Object * obj)179 static void zynqmp_apu_init(Object *obj)
180 {
181     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
182     int i;
183 
184     s->reg_array =
185         register_init_block32(DEVICE(obj), zynqmp_apu_regs_info,
186                               ARRAY_SIZE(zynqmp_apu_regs_info),
187                               s->regs_info, s->regs,
188                               &zynqmp_apu_ops,
189                               XILINX_ZYNQMP_APU_ERR_DEBUG,
190                               APU_R_MAX * 4);
191     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->reg_array->mem);
192     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq_imr);
193 
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);
200     }
201 
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);
209 }
210 
zynqmp_apu_finalize(Object * obj)211 static void zynqmp_apu_finalize(Object *obj)
212 {
213     XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
214     register_finalize_block(s->reg_array);
215 }
216 
217 static const VMStateDescription vmstate_zynqmp_apu = {
218     .name = TYPE_XLNX_ZYNQMP_APU_CTRL,
219     .version_id = 1,
220     .minimum_version_id = 1,
221     .fields = (const VMStateField[]) {
222         VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPAPUCtrl, APU_R_MAX),
223         VMSTATE_END_OF_LIST(),
224     }
225 };
226 
zynqmp_apu_class_init(ObjectClass * klass,void * data)227 static void zynqmp_apu_class_init(ObjectClass *klass, void *data)
228 {
229     ResettableClass *rc = RESETTABLE_CLASS(klass);
230     DeviceClass *dc = DEVICE_CLASS(klass);
231 
232     dc->vmsd = &vmstate_zynqmp_apu;
233 
234     rc->phases.enter = zynqmp_apu_reset_enter;
235     rc->phases.hold = zynqmp_apu_reset_hold;
236 }
237 
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,
245 };
246 
zynqmp_apu_register_types(void)247 static void zynqmp_apu_register_types(void)
248 {
249     type_register_static(&zynqmp_apu_info);
250 }
251 
252 type_init(zynqmp_apu_register_types)
253