2 * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
3 * Copyright (c) 2015, Microsoft
4 * Written by Andrew Baumann
6 * Fairly hacky. Based on gutted code for pl011 emulation (copyright below).
10 * Arm PrimeCell PL011 UART
12 * Copyright (c) 2006 CodeSourcery.
13 * Written by Paul Brook
15 * This code is licensed under the GPL.
18 #include "hw/char/bcm2835_aux.h"
20 static void bcm2835_aux_update(BCM2835AuxState
*s
)
22 bool status
= (s
->rx_int_enable
&& s
->read_count
!= 0) || s
->tx_int_enable
;
23 qemu_set_irq(s
->irq
, status
);
26 static uint64_t bcm2835_aux_read(void *opaque
, hwaddr offset
,
29 BCM2835AuxState
*s
= (BCM2835AuxState
*)opaque
;
32 switch (offset
>> 2) {
34 return 1; /* mini UART enabled */
36 case 16: /* AUX_MU_IO_REG */
37 c
= s
->read_fifo
[s
->read_pos
];
38 if (s
->read_count
> 0) {
40 if (++s
->read_pos
== 8) {
45 qemu_chr_accept_input(s
->chr
);
47 bcm2835_aux_update(s
);
50 case 17: /* AUX_MU_IIR_REG */
52 if (s
->rx_int_enable
) {
55 if (s
->tx_int_enable
) {
60 case 18: /* AUX_MU_IER_REG */
62 if (s
->tx_int_enable
) {
64 } else if (s
->rx_int_enable
&& s
->read_count
!= 0) {
69 case 21: /* AUX_MU_LSR_REG */
70 res
= 0x60; /* tx idle, empty */
71 if (s
->read_count
!= 0) {
76 case 25: /* AUX_MU_STAT_REG */
77 res
= 0x302; /* space in the output buffer, empty tx fifo */
78 if (s
->read_count
> 0) {
79 res
|= 0x1; /* data in input buffer */
80 assert(s
->read_count
< 8);
81 res
|= ((uint32_t)s
->read_count
) << 16; /* rx fifo fill level */
86 qemu_log_mask(LOG_GUEST_ERROR
,
87 "bcm2835_aux_read: Bad offset %x\n", (int)offset
);
92 static void bcm2835_aux_write(void *opaque
, hwaddr offset
,
93 uint64_t value
, unsigned size
)
95 BCM2835AuxState
*s
= (BCM2835AuxState
*)opaque
;
98 switch (offset
>> 2) {
101 qemu_log_mask(LOG_GUEST_ERROR
,
102 "bcm2835_aux_write: Trying to enable SPI or"
103 " disable UART. Not supported!\n");
107 case 16: /* AUX_MU_IO_REG */
110 qemu_chr_fe_write(s
->chr
, &ch
, 1);
114 case 17: /* AUX_MU_IIR_REG */
115 s
->rx_int_enable
= (value
& 0x2) != 0;
116 s
->tx_int_enable
= (value
& 0x1) != 0;
119 case 18: /* AUX_MU_IER_REG */
126 qemu_log_mask(LOG_GUEST_ERROR
,
127 "bcm2835_aux_write: Bad offset %x\n", (int)offset
);
130 bcm2835_aux_update(s
);
133 static int bcm2835_aux_can_receive(void *opaque
)
135 BCM2835AuxState
*s
= (BCM2835AuxState
*)opaque
;
137 return s
->read_count
< 8;
140 static void bcm2835_aux_put_fifo(void *opaque
, uint32_t value
)
142 BCM2835AuxState
*s
= (BCM2835AuxState
*)opaque
;
145 slot
= s
->read_pos
+ s
->read_count
;
149 s
->read_fifo
[slot
] = value
;
151 if (s
->read_count
== 8) {
154 bcm2835_aux_update(s
);
157 static void bcm2835_aux_receive(void *opaque
, const uint8_t *buf
, int size
)
159 bcm2835_aux_put_fifo(opaque
, *buf
);
162 static void bcm2835_aux_event(void *opaque
, int event
)
164 if (event
== CHR_EVENT_BREAK
) {
165 bcm2835_aux_put_fifo(opaque
, 0x400);
169 static const MemoryRegionOps bcm2835_aux_ops
= {
170 .read
= bcm2835_aux_read
,
171 .write
= bcm2835_aux_write
,
172 .endianness
= DEVICE_NATIVE_ENDIAN
,
175 static const VMStateDescription vmstate_bcm2835_aux
= {
176 .name
= "bcm2835_aux",
178 .minimum_version_id
= 2,
179 .fields
= (VMStateField
[]) {
180 VMSTATE_UINT32_ARRAY(read_fifo
, BCM2835AuxState
, 8),
181 VMSTATE_INT32(read_pos
, BCM2835AuxState
),
182 VMSTATE_INT32(read_count
, BCM2835AuxState
),
183 VMSTATE_END_OF_LIST()
187 static void bcm2835_aux_init(Object
*obj
)
189 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
190 BCM2835AuxState
*s
= BCM2835_AUX(obj
);
192 memory_region_init_io(&s
->iomem
, OBJECT(s
), &bcm2835_aux_ops
, s
,
193 "bcm2835_aux", 0x100);
194 sysbus_init_mmio(sbd
, &s
->iomem
);
195 sysbus_init_irq(sbd
, &s
->irq
);
198 static void bcm2835_aux_realize(DeviceState
*dev
, Error
**errp
)
200 BCM2835AuxState
*s
= BCM2835_AUX(dev
);
202 /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
203 s
->chr
= qemu_char_get_next_serial();
206 qemu_chr_add_handlers(s
->chr
, bcm2835_aux_can_receive
,
207 bcm2835_aux_receive
, bcm2835_aux_event
, s
);
211 static void bcm2835_aux_class_init(ObjectClass
*oc
, void *data
)
213 DeviceClass
*dc
= DEVICE_CLASS(oc
);
215 dc
->realize
= bcm2835_aux_realize
;
216 dc
->vmsd
= &vmstate_bcm2835_aux
;
217 /* Reason: realize() method uses qemu_char_get_next_serial() */
218 dc
->cannot_instantiate_with_device_add_yet
= true;
221 static const TypeInfo bcm2835_aux_info
= {
222 .name
= TYPE_BCM2835_AUX
,
223 .parent
= TYPE_SYS_BUS_DEVICE
,
224 .instance_size
= sizeof(BCM2835AuxState
),
225 .instance_init
= bcm2835_aux_init
,
226 .class_init
= bcm2835_aux_class_init
,
229 static void bcm2835_aux_register_types(void)
231 type_register_static(&bcm2835_aux_info
);
234 type_init(bcm2835_aux_register_types
)