xref: /openbmc/qemu/hw/misc/xlnx-zynqmp-apu-ctrl.c (revision 83baec642a13a69398a2643a1f905606c13cd363)
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