2 * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
3 * Copyright (c) 2015, Microsoft
4 * Written by Andrew Baumann
5 * Based on pl011.c, copyright terms below:
7 * Arm PrimeCell PL011 UART
9 * Copyright (c) 2006 CodeSourcery.
10 * Written by Paul Brook
12 * This code is licensed under the GPL.
14 * At present only the core UART functions (data path for tx/rx) are
15 * implemented. The following features/registers are unimplemented:
16 * - Line/modem control
23 #include "qemu/osdep.h"
24 #include "hw/char/bcm2835_aux.h"
26 #include "hw/qdev-properties.h"
27 #include "hw/qdev-properties-system.h"
28 #include "migration/vmstate.h"
30 #include "qemu/module.h"
33 #define AUX_ENABLES 0x4
34 #define AUX_MU_IO_REG 0x40
35 #define AUX_MU_IER_REG 0x44
36 #define AUX_MU_IIR_REG 0x48
37 #define AUX_MU_LCR_REG 0x4c
38 #define AUX_MU_MCR_REG 0x50
39 #define AUX_MU_LSR_REG 0x54
40 #define AUX_MU_MSR_REG 0x58
41 #define AUX_MU_SCRATCH 0x5c
42 #define AUX_MU_CNTL_REG 0x60
43 #define AUX_MU_STAT_REG 0x64
44 #define AUX_MU_BAUD_REG 0x68
46 /* bits in IER/IIR registers */
50 static void bcm2835_aux_update(BCM2835AuxState
*s
)
52 /* signal an interrupt if either:
53 * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
54 * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
57 if ((s
->ier
& RX_INT
) && s
->read_count
!= 0) {
60 if (s
->ier
& TX_INT
) {
63 qemu_set_irq(s
->irq
, s
->iir
!= 0);
66 static uint64_t bcm2835_aux_read(void *opaque
, hwaddr offset
, unsigned size
)
68 BCM2835AuxState
*s
= opaque
;
76 return 1; /* mini UART permanently enabled */
79 /* "DLAB bit set means access baudrate register" is NYI */
80 c
= s
->read_fifo
[s
->read_pos
];
81 if (s
->read_count
> 0) {
83 if (++s
->read_pos
== BCM2835_AUX_RX_FIFO_LEN
) {
87 qemu_chr_fe_accept_input(&s
->chr
);
88 bcm2835_aux_update(s
);
92 /* "DLAB bit set means access baudrate register" is NYI */
93 return 0xc0 | s
->ier
; /* FIFO enables always read 1 */
96 res
= 0xc0; /* FIFO enables */
97 /* The spec is unclear on what happens when both tx and rx
98 * interrupts are active, besides that this cannot occur. At
99 * present, we choose to prioritise the rx interrupt, since
100 * the tx fifo is always empty. */
101 if (s
->read_count
!= 0) {
112 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_LCR_REG unsupported\n", __func__
);
116 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_MCR_REG unsupported\n", __func__
);
120 res
= 0x60; /* tx idle, empty */
121 if (s
->read_count
!= 0) {
127 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_MSR_REG unsupported\n", __func__
);
131 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_SCRATCH unsupported\n", __func__
);
134 case AUX_MU_CNTL_REG
:
135 return 0x3; /* tx, rx enabled */
137 case AUX_MU_STAT_REG
:
138 res
= 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
139 if (s
->read_count
> 0) {
140 res
|= 0x1; /* data in input buffer */
141 assert(s
->read_count
< BCM2835_AUX_RX_FIFO_LEN
);
142 res
|= ((uint32_t)s
->read_count
) << 16; /* rx fifo fill level */
146 case AUX_MU_BAUD_REG
:
147 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_BAUD_REG unsupported\n", __func__
);
151 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
157 static void bcm2835_aux_write(void *opaque
, hwaddr offset
, uint64_t value
,
160 BCM2835AuxState
*s
= opaque
;
166 qemu_log_mask(LOG_UNIMP
, "%s: unsupported attempt to enable SPI"
167 " or disable UART: 0x%"PRIx64
"\n",
173 /* "DLAB bit set means access baudrate register" is NYI */
175 /* XXX this blocks entire thread. Rewrite to use
176 * qemu_chr_fe_write and background I/O callbacks */
177 qemu_chr_fe_write_all(&s
->chr
, &ch
, 1);
181 /* "DLAB bit set means access baudrate register" is NYI */
182 s
->ier
= value
& (TX_INT
| RX_INT
);
183 bcm2835_aux_update(s
);
193 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_LCR_REG unsupported\n", __func__
);
197 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_MCR_REG unsupported\n", __func__
);
201 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_SCRATCH unsupported\n", __func__
);
204 case AUX_MU_CNTL_REG
:
205 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_CNTL_REG unsupported\n", __func__
);
208 case AUX_MU_BAUD_REG
:
209 qemu_log_mask(LOG_UNIMP
, "%s: AUX_MU_BAUD_REG unsupported\n", __func__
);
213 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
217 bcm2835_aux_update(s
);
220 static int bcm2835_aux_can_receive(void *opaque
)
222 BCM2835AuxState
*s
= opaque
;
224 return s
->read_count
< BCM2835_AUX_RX_FIFO_LEN
;
227 static void bcm2835_aux_put_fifo(void *opaque
, uint8_t value
)
229 BCM2835AuxState
*s
= opaque
;
232 slot
= s
->read_pos
+ s
->read_count
;
233 if (slot
>= BCM2835_AUX_RX_FIFO_LEN
) {
234 slot
-= BCM2835_AUX_RX_FIFO_LEN
;
236 s
->read_fifo
[slot
] = value
;
238 if (s
->read_count
== BCM2835_AUX_RX_FIFO_LEN
) {
241 bcm2835_aux_update(s
);
244 static void bcm2835_aux_receive(void *opaque
, const uint8_t *buf
, int size
)
246 bcm2835_aux_put_fifo(opaque
, *buf
);
249 static const MemoryRegionOps bcm2835_aux_ops
= {
250 .read
= bcm2835_aux_read
,
251 .write
= bcm2835_aux_write
,
252 .endianness
= DEVICE_NATIVE_ENDIAN
,
253 .impl
.min_access_size
= 4,
254 .impl
.max_access_size
= 4,
255 .valid
.min_access_size
= 1,
256 .valid
.max_access_size
= 4,
259 static const VMStateDescription vmstate_bcm2835_aux
= {
260 .name
= TYPE_BCM2835_AUX
,
262 .minimum_version_id
= 1,
263 .fields
= (VMStateField
[]) {
264 VMSTATE_UINT8_ARRAY(read_fifo
, BCM2835AuxState
,
265 BCM2835_AUX_RX_FIFO_LEN
),
266 VMSTATE_UINT8(read_pos
, BCM2835AuxState
),
267 VMSTATE_UINT8(read_count
, BCM2835AuxState
),
268 VMSTATE_UINT8(ier
, BCM2835AuxState
),
269 VMSTATE_UINT8(iir
, BCM2835AuxState
),
270 VMSTATE_END_OF_LIST()
274 static void bcm2835_aux_init(Object
*obj
)
276 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
277 BCM2835AuxState
*s
= BCM2835_AUX(obj
);
279 memory_region_init_io(&s
->iomem
, OBJECT(s
), &bcm2835_aux_ops
, s
,
280 TYPE_BCM2835_AUX
, 0x100);
281 sysbus_init_mmio(sbd
, &s
->iomem
);
282 sysbus_init_irq(sbd
, &s
->irq
);
285 static void bcm2835_aux_realize(DeviceState
*dev
, Error
**errp
)
287 BCM2835AuxState
*s
= BCM2835_AUX(dev
);
289 qemu_chr_fe_set_handlers(&s
->chr
, bcm2835_aux_can_receive
,
290 bcm2835_aux_receive
, NULL
, NULL
, s
, NULL
, true);
293 static Property bcm2835_aux_props
[] = {
294 DEFINE_PROP_CHR("chardev", BCM2835AuxState
, chr
),
295 DEFINE_PROP_END_OF_LIST(),
298 static void bcm2835_aux_class_init(ObjectClass
*oc
, void *data
)
300 DeviceClass
*dc
= DEVICE_CLASS(oc
);
302 dc
->realize
= bcm2835_aux_realize
;
303 dc
->vmsd
= &vmstate_bcm2835_aux
;
304 set_bit(DEVICE_CATEGORY_INPUT
, dc
->categories
);
305 device_class_set_props(dc
, bcm2835_aux_props
);
308 static const TypeInfo bcm2835_aux_info
= {
309 .name
= TYPE_BCM2835_AUX
,
310 .parent
= TYPE_SYS_BUS_DEVICE
,
311 .instance_size
= sizeof(BCM2835AuxState
),
312 .instance_init
= bcm2835_aux_init
,
313 .class_init
= bcm2835_aux_class_init
,
316 static void bcm2835_aux_register_types(void)
318 type_register_static(&bcm2835_aux_info
);
321 type_init(bcm2835_aux_register_types
)