1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include "qemu/osdep.h"
27#include "hw/sysbus.h"
28#include "hw/register.h"
29#include "hw/ptimer.h"
30#include "qemu/bitops.h"
31#include "qapi/error.h"
32#include "sysemu/dma.h"
33#include "qemu/log.h"
34#include "qemu/main-loop.h"
35#include "qom/cpu.h"
36
37#include "hw/misc/xlnx-zynqmp-pmufw-cfg.h"
38
39#ifndef XILINX_ZYNQMP_BOOT_DEBUG
40#define XILINX_ZYNQMP_BOOT_DEBUG 0
41#endif
42
43#define TYPE_XILINX_ZYNQMP_BOOT "xlnx,zynqmp-boot"
44
45#define XILINX_ZYNQMP_BOOT(obj) \
46 OBJECT_CHECK(ZynqMPBoot, (obj), TYPE_XILINX_ZYNQMP_BOOT)
47
48
49#define IPI_BUFFER_BASEADDR 0xFF990000U
50#define IPI_BUFFER_RPU_0_BASE (IPI_BUFFER_BASEADDR + 0x0U)
51#define IPI_BUFFER_RPU_1_BASE (IPI_BUFFER_BASEADDR + 0x200U)
52#define IPI_BUFFER_APU_BASE (IPI_BUFFER_BASEADDR + 0x400U)
53#define IPI_BUFFER_PMU_BASE (IPI_BUFFER_BASEADDR + 0xE00U)
54
55#define IPI_BUFFER_TARGET_PMU_OFFSET 0x1C0U
56
57#define IPI_BUFFER_REQ_OFFSET 0x0U
58#define IPI_BUFFER_RESP_OFFSET 0x20U
59
60
61#define IPI_BASEADDR 0XFF300000
62#define IPI_APU_IXR_PMU_0_MASK (1 << 16)
63
64#define IPI_TRIG_OFFSET 0
65#define IPI_OBS_OFFSET 4
66
67
68#define PM_INT_NUM 0
69#define IPI_PMU_PM_INT_MASK (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
70
71#define IPI_APU_MASK 1U
72
73#define PAYLOAD_ARG_CNT 6
74#define PM_SET_CONFIGURATION 2
75
76#define CPU_NONE 0xFFFFFFFF
77
78#define DB_PRINT_L(lvl, fmt, args...) do { \
79 if (XILINX_ZYNQMP_BOOT_DEBUG >= lvl) { \
80 qemu_log("%s: " fmt, __func__, ## args); \
81 } \
82} while (0);
83
84#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
85
86typedef enum {
87 STATE_WAIT_RST = 0,
88 STATE_WAIT_PMUFW,
89 STATE_PMUFW_SETCFG,
90 STATE_WAIT_PMUFW_READY,
91 STATE_RELEASE_CPU,
92 STATE_DONE,
93} BootState;
94
95typedef struct ZynqMPBoot {
96 SysBusDevice parent_obj;
97
98 MemoryRegion *dma_mr;
99 AddressSpace *dma_as;
100
101 QEMUBH *bh;
102 ptimer_state *ptimer;
103
104 BootState state;
105
106
107 bool n_reset;
108
109 struct {
110 uint32_t cpu_num;
111 bool use_pmufw;
112 } cfg;
113
114 unsigned char *buf;
115} ZynqMPBoot;
116
117static const MemTxAttrs mattr_secure = { .secure = true };
118
119static void boot_store32(ZynqMPBoot *s, uint64_t addr, uint32_t v)
120{
121 address_space_write(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
122}
123
124static uint32_t boot_load32(ZynqMPBoot *s, uint64_t addr)
125{
126 uint32_t v;
127 address_space_read(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
128 return v;
129}
130
131
132
133
134static bool pm_ipi_ready(ZynqMPBoot *s)
135{
136 uint32_t r;
137
138 r = boot_load32(s, IPI_BASEADDR + IPI_OBS_OFFSET);
139 r &= IPI_PMU_PM_INT_MASK;
140 return !r;
141}
142
143
144
145
146static void pm_ipi_send(ZynqMPBoot *s,
147 uint32_t payload[PAYLOAD_ARG_CNT])
148{
149 unsigned int i;
150 unsigned int offset = 0;
151 uintptr_t buffer_base = IPI_BUFFER_APU_BASE +
152 IPI_BUFFER_TARGET_PMU_OFFSET +
153 IPI_BUFFER_REQ_OFFSET;
154
155 assert(pm_ipi_ready(s));
156
157
158 for (i = 0; i < PAYLOAD_ARG_CNT; i++) {
159 boot_store32(s, buffer_base + offset, payload[i]);
160 offset += 4;
161 }
162
163 boot_store32(s, IPI_BASEADDR + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
164}
165
166static void release_cpu(ZynqMPBoot *s)
167{
168 CPUState *cpu = qemu_get_cpu(s->cfg.cpu_num);
169 CPUClass *cc = CPU_GET_CLASS(cpu);
170 uint64_t pc = 0;
171 uint32_t r;
172
173 DB_PRINT("Starting CPU#%d release\n", s->cfg.cpu_num)
174
175
176
177
178 if (cc->get_pc) {
179 pc = cc->get_pc(cpu);
180 }
181 if (s->cfg.cpu_num < 4) {
182
183 r = boot_load32(s, 0xfd1a0104);
184 r &= ~(1 << s->cfg.cpu_num);
185 boot_store32(s, 0xfd1a0104, 0x8000000e);
186 } else {
187
188 }
189 if (cc->set_pc) {
190 DB_PRINT("Setting CPU#%d PC to 0x%" PRIx64 "\n", s->cfg.cpu_num, pc)
191 cc->set_pc(cpu, pc);
192 }
193}
194
195static bool check_for_pmufw(ZynqMPBoot *s)
196{
197 uint32_t r;
198
199 r = boot_load32(s, 0xFFD80000);
200 return r & (1 << 4);
201}
202
203static void roll_timer(ZynqMPBoot *s)
204{
205 ptimer_set_limit(s->ptimer, 200000, 1);
206 ptimer_run(s->ptimer, 1);
207}
208
209static void boot_sequence(void *opaque)
210{
211 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
212 uint32_t pay[6] = {};
213
214 switch (s->state) {
215 case STATE_WAIT_PMUFW:
216 if (!s->cfg.use_pmufw) {
217 s->state = STATE_RELEASE_CPU;
218 boot_sequence(s);
219 return;
220 }
221
222 if (!check_for_pmufw(s)) {
223 roll_timer(s);
224 return;
225 }
226
227 s->state = STATE_PMUFW_SETCFG;
228 boot_sequence(s);
229 break;
230
231 case STATE_PMUFW_SETCFG:
232 if (!pm_ipi_ready(s)) {
233 roll_timer(s);
234 return;
235 }
236
237
238 s->buf = g_malloc(sizeof pmufw_cfg);
239 address_space_read(s->dma_as, 0, mattr_secure,
240 s->buf, sizeof pmufw_cfg);
241 address_space_write(s->dma_as, 0, mattr_secure,
242 (void *) pmufw_cfg, sizeof pmufw_cfg);
243 pay[0] = PM_SET_CONFIGURATION;
244 pay[1] = 0;
245 pm_ipi_send(s, pay);
246 s->state = STATE_WAIT_PMUFW_READY;
247 boot_sequence(s);
248 break;
249
250 case STATE_WAIT_PMUFW_READY:
251 if (!pm_ipi_ready(s)) {
252 roll_timer(s);
253 return;
254 }
255
256
257 address_space_write(s->dma_as, 0, mattr_secure,
258 s->buf, sizeof pmufw_cfg);
259 g_free(s->buf);
260 s->buf = NULL;
261
262 s->state = STATE_RELEASE_CPU;
263 boot_sequence(s);
264 break;
265
266 case STATE_RELEASE_CPU:
267 release_cpu(s);
268 s->state = STATE_DONE;
269 break;
270
271 case STATE_DONE:
272 case STATE_WAIT_RST:
273
274 g_assert_not_reached();
275 break;
276 };
277}
278
279static void irq_handler(void *opaque, int irq, int level)
280{
281 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
282
283 if (!s->n_reset && level) {
284
285 DB_PRINT("Starting the boot sequence\n");
286 s->state = STATE_WAIT_PMUFW;
287 boot_sequence(s);
288 }
289 s->n_reset = level;
290}
291
292static void zynqmp_boot_realize(DeviceState *dev, Error **errp)
293{
294 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(dev);
295
296 if (s->cfg.cpu_num > 3 && s->cfg.cpu_num != CPU_NONE) {
297 error_setg(errp, "cpu-num %u is out of range\n", s->cfg.cpu_num);
298 }
299
300 s->dma_as = s->dma_mr ? address_space_init_shareable(s->dma_mr, NULL)
301 : &address_space_memory;
302
303 s->bh = qemu_bh_new(boot_sequence, s);
304 s->ptimer = ptimer_init(s->bh);
305 ptimer_set_freq(s->ptimer, 1000000);
306}
307
308static void zynqmp_boot_init(Object *obj)
309{
310 ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(obj);
311
312 qdev_init_gpio_in(DEVICE(obj), irq_handler, 1);
313 object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
314 (Object **)&s->dma_mr,
315 qdev_prop_allow_set_link_before_realize,
316 OBJ_PROP_LINK_UNREF_ON_RELEASE,
317 &error_abort);
318}
319
320static Property zynqmp_boot_props[] = {
321 DEFINE_PROP_UINT32("cpu-num", ZynqMPBoot, cfg.cpu_num, CPU_NONE),
322 DEFINE_PROP_BOOL("use-pmufw", ZynqMPBoot, cfg.use_pmufw, false),
323 DEFINE_PROP_END_OF_LIST(),
324};
325
326static void zynqmp_boot_class_init(ObjectClass *klass, void *data)
327{
328 DeviceClass *dc = DEVICE_CLASS(klass);
329
330 dc->realize = zynqmp_boot_realize;
331 dc->props = zynqmp_boot_props;
332}
333
334static const TypeInfo zynqmp_boot_info = {
335 .name = TYPE_XILINX_ZYNQMP_BOOT,
336 .parent = TYPE_SYS_BUS_DEVICE,
337 .instance_size = sizeof(ZynqMPBoot),
338 .class_init = zynqmp_boot_class_init,
339 .instance_init = zynqmp_boot_init,
340};
341
342static void zynqmp_boot_register_types(void)
343{
344 type_register_static(&zynqmp_boot_info);
345}
346
347type_init(zynqmp_boot_register_types)
348