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
27
28
29
30
31
32#include "qemu/osdep.h"
33#include "hw/irq.h"
34#include "hw/sysbus.h"
35#include "hw/qdev-properties.h"
36#include "migration/vmstate.h"
37#include "qapi/error.h"
38#include "qemu/log.h"
39#include "qemu/module.h"
40#include "qom/object.h"
41#include "trace.h"
42
43static const uint8_t pl061_id[12] =
44 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45static const uint8_t pl061_id_luminary[12] =
46 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
47
48#define TYPE_PL061 "pl061"
49OBJECT_DECLARE_SIMPLE_TYPE(PL061State, PL061)
50
51#define N_GPIOS 8
52
53struct PL061State {
54 SysBusDevice parent_obj;
55
56 MemoryRegion iomem;
57 uint32_t locked;
58 uint32_t data;
59 uint32_t old_out_data;
60 uint32_t old_in_data;
61 uint32_t dir;
62 uint32_t isense;
63 uint32_t ibe;
64 uint32_t iev;
65 uint32_t im;
66 uint32_t istate;
67 uint32_t afsel;
68 uint32_t dr2r;
69 uint32_t dr4r;
70 uint32_t dr8r;
71 uint32_t odr;
72 uint32_t pur;
73 uint32_t pdr;
74 uint32_t slr;
75 uint32_t den;
76 uint32_t cr;
77 uint32_t amsel;
78 qemu_irq irq;
79 qemu_irq out[N_GPIOS];
80 const unsigned char *id;
81
82 uint32_t pullups;
83 uint32_t pulldowns;
84};
85
86static const VMStateDescription vmstate_pl061 = {
87 .name = "pl061",
88 .version_id = 4,
89 .minimum_version_id = 4,
90 .fields = (VMStateField[]) {
91 VMSTATE_UINT32(locked, PL061State),
92 VMSTATE_UINT32(data, PL061State),
93 VMSTATE_UINT32(old_out_data, PL061State),
94 VMSTATE_UINT32(old_in_data, PL061State),
95 VMSTATE_UINT32(dir, PL061State),
96 VMSTATE_UINT32(isense, PL061State),
97 VMSTATE_UINT32(ibe, PL061State),
98 VMSTATE_UINT32(iev, PL061State),
99 VMSTATE_UINT32(im, PL061State),
100 VMSTATE_UINT32(istate, PL061State),
101 VMSTATE_UINT32(afsel, PL061State),
102 VMSTATE_UINT32(dr2r, PL061State),
103 VMSTATE_UINT32(dr4r, PL061State),
104 VMSTATE_UINT32(dr8r, PL061State),
105 VMSTATE_UINT32(odr, PL061State),
106 VMSTATE_UINT32(pur, PL061State),
107 VMSTATE_UINT32(pdr, PL061State),
108 VMSTATE_UINT32(slr, PL061State),
109 VMSTATE_UINT32(den, PL061State),
110 VMSTATE_UINT32(cr, PL061State),
111 VMSTATE_UINT32_V(amsel, PL061State, 2),
112 VMSTATE_END_OF_LIST()
113 }
114};
115
116static uint8_t pl061_floating(PL061State *s)
117{
118
119
120
121
122 uint8_t floating;
123
124 if (s->id == pl061_id_luminary) {
125
126
127
128
129 floating = ~(s->pur | s->pdr);
130 } else {
131 floating = ~(s->pullups | s->pulldowns);
132 }
133 return floating & ~s->dir;
134}
135
136static uint8_t pl061_pullups(PL061State *s)
137{
138
139
140
141
142 uint8_t pullups;
143
144 if (s->id == pl061_id_luminary) {
145
146
147
148
149
150 pullups = s->pur;
151 } else {
152 pullups = s->pullups;
153 }
154 return pullups & ~s->dir;
155}
156
157static void pl061_update(PL061State *s)
158{
159 uint8_t changed;
160 uint8_t mask;
161 uint8_t out;
162 int i;
163 uint8_t pullups = pl061_pullups(s);
164 uint8_t floating = pl061_floating(s);
165
166 trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data,
167 pullups, floating);
168
169
170
171
172
173
174
175 out = (s->data & s->dir) | pullups | (s->old_out_data & floating);
176 changed = s->old_out_data ^ out;
177 if (changed) {
178 s->old_out_data = out;
179 for (i = 0; i < N_GPIOS; i++) {
180 mask = 1 << i;
181 if (changed & mask) {
182 int level = (out & mask) != 0;
183 trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
184 qemu_set_irq(s->out[i], level);
185 }
186 }
187 }
188
189
190 changed = (s->old_in_data ^ s->data) & ~s->dir;
191 if (changed) {
192 s->old_in_data = s->data;
193 for (i = 0; i < N_GPIOS; i++) {
194 mask = 1 << i;
195 if (changed & mask) {
196 trace_pl061_input_change(DEVICE(s)->canonical_path, i,
197 (s->data & mask) != 0);
198
199 if (!(s->isense & mask)) {
200
201 if (s->ibe & mask) {
202
203 s->istate |= mask;
204 } else {
205
206 s->istate |= ~(s->data ^ s->iev) & mask;
207 }
208 }
209 }
210 }
211 }
212
213
214 s->istate |= ~(s->data ^ s->iev) & s->isense;
215
216 trace_pl061_update_istate(DEVICE(s)->canonical_path,
217 s->istate, s->im, (s->istate & s->im) != 0);
218
219 qemu_set_irq(s->irq, (s->istate & s->im) != 0);
220}
221
222static uint64_t pl061_read(void *opaque, hwaddr offset,
223 unsigned size)
224{
225 PL061State *s = (PL061State *)opaque;
226 uint64_t r = 0;
227
228 switch (offset) {
229 case 0x0 ... 0x3ff:
230 r = s->data & (offset >> 2);
231 break;
232 case 0x400:
233 r = s->dir;
234 break;
235 case 0x404:
236 r = s->isense;
237 break;
238 case 0x408:
239 r = s->ibe;
240 break;
241 case 0x40c:
242 r = s->iev;
243 break;
244 case 0x410:
245 r = s->im;
246 break;
247 case 0x414:
248 r = s->istate;
249 break;
250 case 0x418:
251 r = s->istate & s->im;
252 break;
253 case 0x420:
254 r = s->afsel;
255 break;
256 case 0x500:
257 if (s->id != pl061_id_luminary) {
258 goto bad_offset;
259 }
260 r = s->dr2r;
261 break;
262 case 0x504:
263 if (s->id != pl061_id_luminary) {
264 goto bad_offset;
265 }
266 r = s->dr4r;
267 break;
268 case 0x508:
269 if (s->id != pl061_id_luminary) {
270 goto bad_offset;
271 }
272 r = s->dr8r;
273 break;
274 case 0x50c:
275 if (s->id != pl061_id_luminary) {
276 goto bad_offset;
277 }
278 r = s->odr;
279 break;
280 case 0x510:
281 if (s->id != pl061_id_luminary) {
282 goto bad_offset;
283 }
284 r = s->pur;
285 break;
286 case 0x514:
287 if (s->id != pl061_id_luminary) {
288 goto bad_offset;
289 }
290 r = s->pdr;
291 break;
292 case 0x518:
293 if (s->id != pl061_id_luminary) {
294 goto bad_offset;
295 }
296 r = s->slr;
297 break;
298 case 0x51c:
299 if (s->id != pl061_id_luminary) {
300 goto bad_offset;
301 }
302 r = s->den;
303 break;
304 case 0x520:
305 if (s->id != pl061_id_luminary) {
306 goto bad_offset;
307 }
308 r = s->locked;
309 break;
310 case 0x524:
311 if (s->id != pl061_id_luminary) {
312 goto bad_offset;
313 }
314 r = s->cr;
315 break;
316 case 0x528:
317 if (s->id != pl061_id_luminary) {
318 goto bad_offset;
319 }
320 r = s->amsel;
321 break;
322 case 0xfd0 ... 0xfff:
323 r = s->id[(offset - 0xfd0) >> 2];
324 break;
325 default:
326 bad_offset:
327 qemu_log_mask(LOG_GUEST_ERROR,
328 "pl061_read: Bad offset %x\n", (int)offset);
329 break;
330 }
331
332 trace_pl061_read(DEVICE(s)->canonical_path, offset, r);
333 return r;
334}
335
336static void pl061_write(void *opaque, hwaddr offset,
337 uint64_t value, unsigned size)
338{
339 PL061State *s = (PL061State *)opaque;
340 uint8_t mask;
341
342 trace_pl061_write(DEVICE(s)->canonical_path, offset, value);
343
344 switch (offset) {
345 case 0 ... 0x3ff:
346 mask = (offset >> 2) & s->dir;
347 s->data = (s->data & ~mask) | (value & mask);
348 pl061_update(s);
349 return;
350 case 0x400:
351 s->dir = value & 0xff;
352 break;
353 case 0x404:
354 s->isense = value & 0xff;
355 break;
356 case 0x408:
357 s->ibe = value & 0xff;
358 break;
359 case 0x40c:
360 s->iev = value & 0xff;
361 break;
362 case 0x410:
363 s->im = value & 0xff;
364 break;
365 case 0x41c:
366 s->istate &= ~value;
367 break;
368 case 0x420:
369 mask = s->cr;
370 s->afsel = (s->afsel & ~mask) | (value & mask);
371 break;
372 case 0x500:
373 if (s->id != pl061_id_luminary) {
374 goto bad_offset;
375 }
376 s->dr2r = value & 0xff;
377 break;
378 case 0x504:
379 if (s->id != pl061_id_luminary) {
380 goto bad_offset;
381 }
382 s->dr4r = value & 0xff;
383 break;
384 case 0x508:
385 if (s->id != pl061_id_luminary) {
386 goto bad_offset;
387 }
388 s->dr8r = value & 0xff;
389 break;
390 case 0x50c:
391 if (s->id != pl061_id_luminary) {
392 goto bad_offset;
393 }
394 s->odr = value & 0xff;
395 break;
396 case 0x510:
397 if (s->id != pl061_id_luminary) {
398 goto bad_offset;
399 }
400 s->pur = value & 0xff;
401 break;
402 case 0x514:
403 if (s->id != pl061_id_luminary) {
404 goto bad_offset;
405 }
406 s->pdr = value & 0xff;
407 break;
408 case 0x518:
409 if (s->id != pl061_id_luminary) {
410 goto bad_offset;
411 }
412 s->slr = value & 0xff;
413 break;
414 case 0x51c:
415 if (s->id != pl061_id_luminary) {
416 goto bad_offset;
417 }
418 s->den = value & 0xff;
419 break;
420 case 0x520:
421 if (s->id != pl061_id_luminary) {
422 goto bad_offset;
423 }
424 s->locked = (value != 0xacce551);
425 break;
426 case 0x524:
427 if (s->id != pl061_id_luminary) {
428 goto bad_offset;
429 }
430 if (!s->locked)
431 s->cr = value & 0xff;
432 break;
433 case 0x528:
434 if (s->id != pl061_id_luminary) {
435 goto bad_offset;
436 }
437 s->amsel = value & 0xff;
438 break;
439 default:
440 bad_offset:
441 qemu_log_mask(LOG_GUEST_ERROR,
442 "pl061_write: Bad offset %x\n", (int)offset);
443 return;
444 }
445 pl061_update(s);
446 return;
447}
448
449static void pl061_enter_reset(Object *obj, ResetType type)
450{
451 PL061State *s = PL061(obj);
452
453 trace_pl061_reset(DEVICE(s)->canonical_path);
454
455
456
457
458
459
460
461
462
463
464
465 s->data = 0;
466 s->old_in_data = 0;
467 s->dir = 0;
468 s->isense = 0;
469 s->ibe = 0;
470 s->iev = 0;
471 s->im = 0;
472 s->istate = 0;
473 s->afsel = 0;
474 s->dr2r = 0xff;
475 s->dr4r = 0;
476 s->dr8r = 0;
477 s->odr = 0;
478 s->pur = 0;
479 s->pdr = 0;
480 s->slr = 0;
481 s->den = 0;
482 s->locked = 1;
483 s->cr = 0xff;
484 s->amsel = 0;
485}
486
487static void pl061_hold_reset(Object *obj)
488{
489 PL061State *s = PL061(obj);
490 int i, level;
491 uint8_t floating = pl061_floating(s);
492 uint8_t pullups = pl061_pullups(s);
493
494 for (i = 0; i < N_GPIOS; i++) {
495 if (extract32(floating, i, 1)) {
496 continue;
497 }
498 level = extract32(pullups, i, 1);
499 trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
500 qemu_set_irq(s->out[i], level);
501 }
502 s->old_out_data = pullups;
503}
504
505static void pl061_set_irq(void * opaque, int irq, int level)
506{
507 PL061State *s = (PL061State *)opaque;
508 uint8_t mask;
509
510 mask = 1 << irq;
511 if ((s->dir & mask) == 0) {
512 s->data &= ~mask;
513 if (level)
514 s->data |= mask;
515 pl061_update(s);
516 }
517}
518
519static const MemoryRegionOps pl061_ops = {
520 .read = pl061_read,
521 .write = pl061_write,
522 .endianness = DEVICE_NATIVE_ENDIAN,
523};
524
525static void pl061_luminary_init(Object *obj)
526{
527 PL061State *s = PL061(obj);
528
529 s->id = pl061_id_luminary;
530}
531
532static void pl061_init(Object *obj)
533{
534 PL061State *s = PL061(obj);
535 DeviceState *dev = DEVICE(obj);
536 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
537
538 s->id = pl061_id;
539
540 memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000);
541 sysbus_init_mmio(sbd, &s->iomem);
542 sysbus_init_irq(sbd, &s->irq);
543 qdev_init_gpio_in(dev, pl061_set_irq, N_GPIOS);
544 qdev_init_gpio_out(dev, s->out, N_GPIOS);
545}
546
547static void pl061_realize(DeviceState *dev, Error **errp)
548{
549 PL061State *s = PL061(dev);
550
551 if (s->pullups > 0xff) {
552 error_setg(errp, "pullups property must be between 0 and 0xff");
553 return;
554 }
555 if (s->pulldowns > 0xff) {
556 error_setg(errp, "pulldowns property must be between 0 and 0xff");
557 return;
558 }
559 if (s->pullups & s->pulldowns) {
560 error_setg(errp, "no bit may be set both in pullups and pulldowns");
561 return;
562 }
563}
564
565static Property pl061_props[] = {
566 DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff),
567 DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0),
568 DEFINE_PROP_END_OF_LIST()
569};
570
571static void pl061_class_init(ObjectClass *klass, void *data)
572{
573 DeviceClass *dc = DEVICE_CLASS(klass);
574 ResettableClass *rc = RESETTABLE_CLASS(klass);
575
576 dc->vmsd = &vmstate_pl061;
577 dc->realize = pl061_realize;
578 device_class_set_props(dc, pl061_props);
579 rc->phases.enter = pl061_enter_reset;
580 rc->phases.hold = pl061_hold_reset;
581}
582
583static const TypeInfo pl061_info = {
584 .name = TYPE_PL061,
585 .parent = TYPE_SYS_BUS_DEVICE,
586 .instance_size = sizeof(PL061State),
587 .instance_init = pl061_init,
588 .class_init = pl061_class_init,
589};
590
591static const TypeInfo pl061_luminary_info = {
592 .name = "pl061_luminary",
593 .parent = TYPE_PL061,
594 .instance_init = pl061_luminary_init,
595};
596
597static void pl061_register_types(void)
598{
599 type_register_static(&pl061_info);
600 type_register_static(&pl061_luminary_info);
601}
602
603type_init(pl061_register_types)
604