2 * Arm PrimeCell PL061 General Purpose IO with additional
3 * Luminary Micro Stellaris bits.
5 * Copyright (c) 2007 CodeSourcery.
6 * Written by Paul Brook
8 * This code is licensed under the GPL.
11 * + sysbus MMIO region 0: the device registers
12 * + sysbus IRQ: the GPIOINTR interrupt line
13 * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines
14 * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as
16 * + QOM property "pullups": an integer defining whether non-floating lines
17 * configured as inputs should be pulled up to logical 1 (ie whether in
18 * real hardware they have a pullup resistor on the line out of the PL061).
19 * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
20 * be pulled high, bit 1 configures line 1, and so on. The default is 0xff,
21 * indicating that all GPIO lines are pulled up to logical 1.
22 * + QOM property "pulldowns": an integer defining whether non-floating lines
23 * configured as inputs should be pulled down to logical 0 (ie whether in
24 * real hardware they have a pulldown resistor on the line out of the PL061).
25 * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
26 * be pulled low, bit 1 configures line 1, and so on. The default is 0x0.
27 * It is an error to set a bit in both "pullups" and "pulldowns". If a bit
28 * is 0 in both, then the line is considered to be floating, and it will
29 * not have qemu_set_irq() called on it when it is configured as an input.
32 #include "qemu/osdep.h"
34 #include "hw/sysbus.h"
35 #include "hw/qdev-properties.h"
36 #include "migration/vmstate.h"
37 #include "qapi/error.h"
39 #include "qemu/module.h"
40 #include "qom/object.h"
43 static const uint8_t pl061_id
[12] =
44 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45 static const uint8_t pl061_id_luminary
[12] =
46 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
48 #define TYPE_PL061 "pl061"
49 OBJECT_DECLARE_SIMPLE_TYPE(PL061State
, PL061
)
54 SysBusDevice parent_obj
;
59 uint32_t old_out_data
;
79 qemu_irq out
[N_GPIOS
];
80 const unsigned char *id
;
81 /* Properties, for non-Luminary PL061 */
86 static const VMStateDescription vmstate_pl061
= {
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()
116 static uint8_t pl061_floating(PL061State
*s
)
119 * Return mask of bits which correspond to pins configured as inputs
120 * and which are floating (neither pulled up to 1 nor down to 0).
124 if (s
->id
== pl061_id_luminary
) {
126 * If both PUR and PDR bits are clear, there is neither a pullup
127 * nor a pulldown in place, and the output truly floats.
129 floating
= ~(s
->pur
| s
->pdr
);
131 floating
= ~(s
->pullups
| s
->pulldowns
);
133 return floating
& ~s
->dir
;
136 static uint8_t pl061_pullups(PL061State
*s
)
139 * Return mask of bits which correspond to pins configured as inputs
140 * and which are pulled up to 1.
144 if (s
->id
== pl061_id_luminary
) {
146 * The Luminary variant of the PL061 has an extra registers which
147 * the guest can use to configure whether lines should be pullup
152 pullups
= s
->pullups
;
154 return pullups
& ~s
->dir
;
157 static void pl061_update(PL061State
*s
)
163 uint8_t pullups
= pl061_pullups(s
);
164 uint8_t floating
= pl061_floating(s
);
166 trace_pl061_update(DEVICE(s
)->canonical_path
, s
->dir
, s
->data
,
170 * Pins configured as output are driven from the data register;
171 * otherwise if they're pulled up they're 1, and if they're floating
172 * then we give them the same value they had previously, so we don't
173 * report any change to the other end.
175 out
= (s
->data
& s
->dir
) | pullups
| (s
->old_out_data
& floating
);
176 changed
= s
->old_out_data
^ out
;
178 s
->old_out_data
= out
;
179 for (i
= 0; i
< N_GPIOS
; 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
);
190 changed
= (s
->old_in_data
^ s
->data
) & ~s
->dir
;
192 s
->old_in_data
= s
->data
;
193 for (i
= 0; i
< N_GPIOS
; i
++) {
195 if (changed
& mask
) {
196 trace_pl061_input_change(DEVICE(s
)->canonical_path
, i
,
197 (s
->data
& mask
) != 0);
199 if (!(s
->isense
& mask
)) {
202 /* Any edge triggers the interrupt */
205 /* Edge is selected by IEV */
206 s
->istate
|= ~(s
->data
^ s
->iev
) & mask
;
213 /* Level interrupt */
214 s
->istate
|= ~(s
->data
^ s
->iev
) & s
->isense
;
216 trace_pl061_update_istate(DEVICE(s
)->canonical_path
,
217 s
->istate
, s
->im
, (s
->istate
& s
->im
) != 0);
219 qemu_set_irq(s
->irq
, (s
->istate
& s
->im
) != 0);
222 static uint64_t pl061_read(void *opaque
, hwaddr offset
,
225 PL061State
*s
= (PL061State
*)opaque
;
229 case 0x0 ... 0x3ff: /* Data */
230 r
= s
->data
& (offset
>> 2);
232 case 0x400: /* Direction */
235 case 0x404: /* Interrupt sense */
238 case 0x408: /* Interrupt both edges */
241 case 0x40c: /* Interrupt event */
244 case 0x410: /* Interrupt mask */
247 case 0x414: /* Raw interrupt status */
250 case 0x418: /* Masked interrupt status */
251 r
= s
->istate
& s
->im
;
253 case 0x420: /* Alternate function select */
256 case 0x500: /* 2mA drive */
257 if (s
->id
!= pl061_id_luminary
) {
262 case 0x504: /* 4mA drive */
263 if (s
->id
!= pl061_id_luminary
) {
268 case 0x508: /* 8mA drive */
269 if (s
->id
!= pl061_id_luminary
) {
274 case 0x50c: /* Open drain */
275 if (s
->id
!= pl061_id_luminary
) {
280 case 0x510: /* Pull-up */
281 if (s
->id
!= pl061_id_luminary
) {
286 case 0x514: /* Pull-down */
287 if (s
->id
!= pl061_id_luminary
) {
292 case 0x518: /* Slew rate control */
293 if (s
->id
!= pl061_id_luminary
) {
298 case 0x51c: /* Digital enable */
299 if (s
->id
!= pl061_id_luminary
) {
304 case 0x520: /* Lock */
305 if (s
->id
!= pl061_id_luminary
) {
310 case 0x524: /* Commit */
311 if (s
->id
!= pl061_id_luminary
) {
316 case 0x528: /* Analog mode select */
317 if (s
->id
!= pl061_id_luminary
) {
322 case 0xfd0 ... 0xfff: /* ID registers */
323 r
= s
->id
[(offset
- 0xfd0) >> 2];
327 qemu_log_mask(LOG_GUEST_ERROR
,
328 "pl061_read: Bad offset %x\n", (int)offset
);
332 trace_pl061_read(DEVICE(s
)->canonical_path
, offset
, r
);
336 static void pl061_write(void *opaque
, hwaddr offset
,
337 uint64_t value
, unsigned size
)
339 PL061State
*s
= (PL061State
*)opaque
;
342 trace_pl061_write(DEVICE(s
)->canonical_path
, offset
, value
);
346 mask
= (offset
>> 2) & s
->dir
;
347 s
->data
= (s
->data
& ~mask
) | (value
& mask
);
350 case 0x400: /* Direction */
351 s
->dir
= value
& 0xff;
353 case 0x404: /* Interrupt sense */
354 s
->isense
= value
& 0xff;
356 case 0x408: /* Interrupt both edges */
357 s
->ibe
= value
& 0xff;
359 case 0x40c: /* Interrupt event */
360 s
->iev
= value
& 0xff;
362 case 0x410: /* Interrupt mask */
363 s
->im
= value
& 0xff;
365 case 0x41c: /* Interrupt clear */
368 case 0x420: /* Alternate function select */
370 s
->afsel
= (s
->afsel
& ~mask
) | (value
& mask
);
372 case 0x500: /* 2mA drive */
373 if (s
->id
!= pl061_id_luminary
) {
376 s
->dr2r
= value
& 0xff;
378 case 0x504: /* 4mA drive */
379 if (s
->id
!= pl061_id_luminary
) {
382 s
->dr4r
= value
& 0xff;
384 case 0x508: /* 8mA drive */
385 if (s
->id
!= pl061_id_luminary
) {
388 s
->dr8r
= value
& 0xff;
390 case 0x50c: /* Open drain */
391 if (s
->id
!= pl061_id_luminary
) {
394 s
->odr
= value
& 0xff;
396 case 0x510: /* Pull-up */
397 if (s
->id
!= pl061_id_luminary
) {
400 s
->pur
= value
& 0xff;
402 case 0x514: /* Pull-down */
403 if (s
->id
!= pl061_id_luminary
) {
406 s
->pdr
= value
& 0xff;
408 case 0x518: /* Slew rate control */
409 if (s
->id
!= pl061_id_luminary
) {
412 s
->slr
= value
& 0xff;
414 case 0x51c: /* Digital enable */
415 if (s
->id
!= pl061_id_luminary
) {
418 s
->den
= value
& 0xff;
420 case 0x520: /* Lock */
421 if (s
->id
!= pl061_id_luminary
) {
424 s
->locked
= (value
!= 0xacce551);
426 case 0x524: /* Commit */
427 if (s
->id
!= pl061_id_luminary
) {
431 s
->cr
= value
& 0xff;
434 if (s
->id
!= pl061_id_luminary
) {
437 s
->amsel
= value
& 0xff;
441 qemu_log_mask(LOG_GUEST_ERROR
,
442 "pl061_write: Bad offset %x\n", (int)offset
);
449 static void pl061_enter_reset(Object
*obj
, ResetType type
)
451 PL061State
*s
= PL061(obj
);
453 trace_pl061_reset(DEVICE(s
)->canonical_path
);
455 /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */
458 * FIXME: For the LM3S6965, not all of the PL061 instances have the
459 * same reset values for GPIOPUR, GPIOAFSEL and GPIODEN, so in theory
460 * we should allow the board to configure these via properties.
461 * In practice, we don't wire anything up to the affected GPIO lines
462 * (PB7, PC0, PC1, PC2, PC3 -- they're used for JTAG), so we can
463 * get away with this inaccuracy.
487 static void pl061_hold_reset(Object
*obj
)
489 PL061State
*s
= PL061(obj
);
491 uint8_t floating
= pl061_floating(s
);
492 uint8_t pullups
= pl061_pullups(s
);
494 for (i
= 0; i
< N_GPIOS
; i
++) {
495 if (extract32(floating
, i
, 1)) {
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
);
502 s
->old_out_data
= pullups
;
505 static void pl061_set_irq(void * opaque
, int irq
, int level
)
507 PL061State
*s
= (PL061State
*)opaque
;
511 if ((s
->dir
& mask
) == 0) {
519 static const MemoryRegionOps pl061_ops
= {
521 .write
= pl061_write
,
522 .endianness
= DEVICE_NATIVE_ENDIAN
,
525 static void pl061_luminary_init(Object
*obj
)
527 PL061State
*s
= PL061(obj
);
529 s
->id
= pl061_id_luminary
;
532 static void pl061_init(Object
*obj
)
534 PL061State
*s
= PL061(obj
);
535 DeviceState
*dev
= DEVICE(obj
);
536 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
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
);
547 static void pl061_realize(DeviceState
*dev
, Error
**errp
)
549 PL061State
*s
= PL061(dev
);
551 if (s
->pullups
> 0xff) {
552 error_setg(errp
, "pullups property must be between 0 and 0xff");
555 if (s
->pulldowns
> 0xff) {
556 error_setg(errp
, "pulldowns property must be between 0 and 0xff");
559 if (s
->pullups
& s
->pulldowns
) {
560 error_setg(errp
, "no bit may be set both in pullups and pulldowns");
565 static 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()
571 static void pl061_class_init(ObjectClass
*klass
, void *data
)
573 DeviceClass
*dc
= DEVICE_CLASS(klass
);
574 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
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
;
583 static const TypeInfo pl061_info
= {
585 .parent
= TYPE_SYS_BUS_DEVICE
,
586 .instance_size
= sizeof(PL061State
),
587 .instance_init
= pl061_init
,
588 .class_init
= pl061_class_init
,
591 static const TypeInfo pl061_luminary_info
= {
592 .name
= "pl061_luminary",
593 .parent
= TYPE_PL061
,
594 .instance_init
= pl061_luminary_init
,
597 static void pl061_register_types(void)
599 type_register_static(&pl061_info
);
600 type_register_static(&pl061_luminary_info
);
603 type_init(pl061_register_types
)