xref: /qemu/hw/char/bcm2835_aux.c (revision 4f67d30b5e74e060b8dbe10528829b47345cd6e8)
197398d90SAndrew Baumann /*
297398d90SAndrew Baumann  * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
397398d90SAndrew Baumann  * Copyright (c) 2015, Microsoft
497398d90SAndrew Baumann  * Written by Andrew Baumann
597398d90SAndrew Baumann  * Based on pl011.c, copyright terms below:
697398d90SAndrew Baumann  *
797398d90SAndrew Baumann  * Arm PrimeCell PL011 UART
897398d90SAndrew Baumann  *
997398d90SAndrew Baumann  * Copyright (c) 2006 CodeSourcery.
1097398d90SAndrew Baumann  * Written by Paul Brook
1197398d90SAndrew Baumann  *
1297398d90SAndrew Baumann  * This code is licensed under the GPL.
1397398d90SAndrew Baumann  *
1497398d90SAndrew Baumann  * At present only the core UART functions (data path for tx/rx) are
1597398d90SAndrew Baumann  * implemented. The following features/registers are unimplemented:
1697398d90SAndrew Baumann  *  - Line/modem control
1797398d90SAndrew Baumann  *  - Scratch register
1897398d90SAndrew Baumann  *  - Extra control
1997398d90SAndrew Baumann  *  - Baudrate
2097398d90SAndrew Baumann  *  - SPI interfaces
2197398d90SAndrew Baumann  */
2297398d90SAndrew Baumann 
2397398d90SAndrew Baumann #include "qemu/osdep.h"
2497398d90SAndrew Baumann #include "hw/char/bcm2835_aux.h"
2564552b6bSMarkus Armbruster #include "hw/irq.h"
26a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
27d6454270SMarkus Armbruster #include "migration/vmstate.h"
2803dd024fSPaolo Bonzini #include "qemu/log.h"
290b8fa32fSMarkus Armbruster #include "qemu/module.h"
3097398d90SAndrew Baumann 
3197398d90SAndrew Baumann #define AUX_IRQ         0x0
3297398d90SAndrew Baumann #define AUX_ENABLES     0x4
3397398d90SAndrew Baumann #define AUX_MU_IO_REG   0x40
3497398d90SAndrew Baumann #define AUX_MU_IER_REG  0x44
3597398d90SAndrew Baumann #define AUX_MU_IIR_REG  0x48
3697398d90SAndrew Baumann #define AUX_MU_LCR_REG  0x4c
3797398d90SAndrew Baumann #define AUX_MU_MCR_REG  0x50
3897398d90SAndrew Baumann #define AUX_MU_LSR_REG  0x54
3997398d90SAndrew Baumann #define AUX_MU_MSR_REG  0x58
4097398d90SAndrew Baumann #define AUX_MU_SCRATCH  0x5c
4197398d90SAndrew Baumann #define AUX_MU_CNTL_REG 0x60
4297398d90SAndrew Baumann #define AUX_MU_STAT_REG 0x64
4397398d90SAndrew Baumann #define AUX_MU_BAUD_REG 0x68
4497398d90SAndrew Baumann 
4597398d90SAndrew Baumann /* bits in IER/IIR registers */
4665e9f27fSGuenter Roeck #define RX_INT  0x1
4765e9f27fSGuenter Roeck #define TX_INT  0x2
4897398d90SAndrew Baumann 
4997398d90SAndrew Baumann static void bcm2835_aux_update(BCM2835AuxState *s)
5097398d90SAndrew Baumann {
5197398d90SAndrew Baumann     /* signal an interrupt if either:
5297398d90SAndrew Baumann      * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
5397398d90SAndrew Baumann      * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
5497398d90SAndrew Baumann      */
5597398d90SAndrew Baumann     s->iir = 0;
5697398d90SAndrew Baumann     if ((s->ier & RX_INT) && s->read_count != 0) {
5797398d90SAndrew Baumann         s->iir |= RX_INT;
5897398d90SAndrew Baumann     }
5997398d90SAndrew Baumann     if (s->ier & TX_INT) {
6097398d90SAndrew Baumann         s->iir |= TX_INT;
6197398d90SAndrew Baumann     }
6297398d90SAndrew Baumann     qemu_set_irq(s->irq, s->iir != 0);
6397398d90SAndrew Baumann }
6497398d90SAndrew Baumann 
6597398d90SAndrew Baumann static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
6697398d90SAndrew Baumann {
6797398d90SAndrew Baumann     BCM2835AuxState *s = opaque;
6897398d90SAndrew Baumann     uint32_t c, res;
6997398d90SAndrew Baumann 
7097398d90SAndrew Baumann     switch (offset) {
7197398d90SAndrew Baumann     case AUX_IRQ:
7297398d90SAndrew Baumann         return s->iir != 0;
7397398d90SAndrew Baumann 
7497398d90SAndrew Baumann     case AUX_ENABLES:
7597398d90SAndrew Baumann         return 1; /* mini UART permanently enabled */
7697398d90SAndrew Baumann 
7797398d90SAndrew Baumann     case AUX_MU_IO_REG:
7897398d90SAndrew Baumann         /* "DLAB bit set means access baudrate register" is NYI */
7997398d90SAndrew Baumann         c = s->read_fifo[s->read_pos];
8097398d90SAndrew Baumann         if (s->read_count > 0) {
8197398d90SAndrew Baumann             s->read_count--;
8297398d90SAndrew Baumann             if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
8397398d90SAndrew Baumann                 s->read_pos = 0;
8497398d90SAndrew Baumann             }
8597398d90SAndrew Baumann         }
865345fdb4SMarc-André Lureau         qemu_chr_fe_accept_input(&s->chr);
8797398d90SAndrew Baumann         bcm2835_aux_update(s);
8897398d90SAndrew Baumann         return c;
8997398d90SAndrew Baumann 
9097398d90SAndrew Baumann     case AUX_MU_IER_REG:
9197398d90SAndrew Baumann         /* "DLAB bit set means access baudrate register" is NYI */
9297398d90SAndrew Baumann         return 0xc0 | s->ier; /* FIFO enables always read 1 */
9397398d90SAndrew Baumann 
9497398d90SAndrew Baumann     case AUX_MU_IIR_REG:
9597398d90SAndrew Baumann         res = 0xc0; /* FIFO enables */
9697398d90SAndrew Baumann         /* The spec is unclear on what happens when both tx and rx
9797398d90SAndrew Baumann          * interrupts are active, besides that this cannot occur. At
9897398d90SAndrew Baumann          * present, we choose to prioritise the rx interrupt, since
9997398d90SAndrew Baumann          * the tx fifo is always empty. */
10097398d90SAndrew Baumann         if (s->read_count != 0) {
10197398d90SAndrew Baumann             res |= 0x4;
10297398d90SAndrew Baumann         } else {
10397398d90SAndrew Baumann             res |= 0x2;
10497398d90SAndrew Baumann         }
10597398d90SAndrew Baumann         if (s->iir == 0) {
10697398d90SAndrew Baumann             res |= 0x1;
10797398d90SAndrew Baumann         }
10897398d90SAndrew Baumann         return res;
10997398d90SAndrew Baumann 
11097398d90SAndrew Baumann     case AUX_MU_LCR_REG:
11197398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
11297398d90SAndrew Baumann         return 0;
11397398d90SAndrew Baumann 
11497398d90SAndrew Baumann     case AUX_MU_MCR_REG:
11597398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
11697398d90SAndrew Baumann         return 0;
11797398d90SAndrew Baumann 
11897398d90SAndrew Baumann     case AUX_MU_LSR_REG:
11997398d90SAndrew Baumann         res = 0x60; /* tx idle, empty */
12097398d90SAndrew Baumann         if (s->read_count != 0) {
12197398d90SAndrew Baumann             res |= 0x1;
12297398d90SAndrew Baumann         }
12397398d90SAndrew Baumann         return res;
12497398d90SAndrew Baumann 
12597398d90SAndrew Baumann     case AUX_MU_MSR_REG:
12697398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
12797398d90SAndrew Baumann         return 0;
12897398d90SAndrew Baumann 
12997398d90SAndrew Baumann     case AUX_MU_SCRATCH:
13097398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
13197398d90SAndrew Baumann         return 0;
13297398d90SAndrew Baumann 
13397398d90SAndrew Baumann     case AUX_MU_CNTL_REG:
13497398d90SAndrew Baumann         return 0x3; /* tx, rx enabled */
13597398d90SAndrew Baumann 
13697398d90SAndrew Baumann     case AUX_MU_STAT_REG:
13797398d90SAndrew Baumann         res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
13897398d90SAndrew Baumann         if (s->read_count > 0) {
13997398d90SAndrew Baumann             res |= 0x1; /* data in input buffer */
14097398d90SAndrew Baumann             assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
14197398d90SAndrew Baumann             res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
14297398d90SAndrew Baumann         }
14397398d90SAndrew Baumann         return res;
14497398d90SAndrew Baumann 
14597398d90SAndrew Baumann     case AUX_MU_BAUD_REG:
14697398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
14797398d90SAndrew Baumann         return 0;
14897398d90SAndrew Baumann 
14997398d90SAndrew Baumann     default:
15097398d90SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
15197398d90SAndrew Baumann                       __func__, offset);
15297398d90SAndrew Baumann         return 0;
15397398d90SAndrew Baumann     }
15497398d90SAndrew Baumann }
15597398d90SAndrew Baumann 
15697398d90SAndrew Baumann static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
15797398d90SAndrew Baumann                               unsigned size)
15897398d90SAndrew Baumann {
15997398d90SAndrew Baumann     BCM2835AuxState *s = opaque;
16097398d90SAndrew Baumann     unsigned char ch;
16197398d90SAndrew Baumann 
16297398d90SAndrew Baumann     switch (offset) {
16397398d90SAndrew Baumann     case AUX_ENABLES:
16497398d90SAndrew Baumann         if (value != 1) {
16597398d90SAndrew Baumann             qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI"
166e1ecf8c8SPhilippe Mathieu-Daudé                                      " or disable UART: 0x%"PRIx64"\n",
167e1ecf8c8SPhilippe Mathieu-Daudé                           __func__, value);
16897398d90SAndrew Baumann         }
16997398d90SAndrew Baumann         break;
17097398d90SAndrew Baumann 
17197398d90SAndrew Baumann     case AUX_MU_IO_REG:
17297398d90SAndrew Baumann         /* "DLAB bit set means access baudrate register" is NYI */
17397398d90SAndrew Baumann         ch = value;
1746ab3fc32SDaniel P. Berrange         /* XXX this blocks entire thread. Rewrite to use
1756ab3fc32SDaniel P. Berrange          * qemu_chr_fe_write and background I/O callbacks */
1765345fdb4SMarc-André Lureau         qemu_chr_fe_write_all(&s->chr, &ch, 1);
17797398d90SAndrew Baumann         break;
17897398d90SAndrew Baumann 
17997398d90SAndrew Baumann     case AUX_MU_IER_REG:
18097398d90SAndrew Baumann         /* "DLAB bit set means access baudrate register" is NYI */
18197398d90SAndrew Baumann         s->ier = value & (TX_INT | RX_INT);
18297398d90SAndrew Baumann         bcm2835_aux_update(s);
18397398d90SAndrew Baumann         break;
18497398d90SAndrew Baumann 
18597398d90SAndrew Baumann     case AUX_MU_IIR_REG:
18697398d90SAndrew Baumann         if (value & 0x2) {
18797398d90SAndrew Baumann             s->read_count = 0;
18897398d90SAndrew Baumann         }
18997398d90SAndrew Baumann         break;
19097398d90SAndrew Baumann 
19197398d90SAndrew Baumann     case AUX_MU_LCR_REG:
19297398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
19397398d90SAndrew Baumann         break;
19497398d90SAndrew Baumann 
19597398d90SAndrew Baumann     case AUX_MU_MCR_REG:
19697398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
19797398d90SAndrew Baumann         break;
19897398d90SAndrew Baumann 
19997398d90SAndrew Baumann     case AUX_MU_SCRATCH:
20097398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
20197398d90SAndrew Baumann         break;
20297398d90SAndrew Baumann 
20397398d90SAndrew Baumann     case AUX_MU_CNTL_REG:
20497398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
20597398d90SAndrew Baumann         break;
20697398d90SAndrew Baumann 
20797398d90SAndrew Baumann     case AUX_MU_BAUD_REG:
20897398d90SAndrew Baumann         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
20997398d90SAndrew Baumann         break;
21097398d90SAndrew Baumann 
21197398d90SAndrew Baumann     default:
21297398d90SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
21397398d90SAndrew Baumann                       __func__, offset);
21497398d90SAndrew Baumann     }
21597398d90SAndrew Baumann 
21697398d90SAndrew Baumann     bcm2835_aux_update(s);
21797398d90SAndrew Baumann }
21897398d90SAndrew Baumann 
21997398d90SAndrew Baumann static int bcm2835_aux_can_receive(void *opaque)
22097398d90SAndrew Baumann {
22197398d90SAndrew Baumann     BCM2835AuxState *s = opaque;
22297398d90SAndrew Baumann 
22397398d90SAndrew Baumann     return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
22497398d90SAndrew Baumann }
22597398d90SAndrew Baumann 
22697398d90SAndrew Baumann static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
22797398d90SAndrew Baumann {
22897398d90SAndrew Baumann     BCM2835AuxState *s = opaque;
22997398d90SAndrew Baumann     int slot;
23097398d90SAndrew Baumann 
23197398d90SAndrew Baumann     slot = s->read_pos + s->read_count;
23297398d90SAndrew Baumann     if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
23397398d90SAndrew Baumann         slot -= BCM2835_AUX_RX_FIFO_LEN;
23497398d90SAndrew Baumann     }
23597398d90SAndrew Baumann     s->read_fifo[slot] = value;
23697398d90SAndrew Baumann     s->read_count++;
23797398d90SAndrew Baumann     if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
23897398d90SAndrew Baumann         /* buffer full */
23997398d90SAndrew Baumann     }
24097398d90SAndrew Baumann     bcm2835_aux_update(s);
24197398d90SAndrew Baumann }
24297398d90SAndrew Baumann 
24397398d90SAndrew Baumann static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
24497398d90SAndrew Baumann {
24597398d90SAndrew Baumann     bcm2835_aux_put_fifo(opaque, *buf);
24697398d90SAndrew Baumann }
24797398d90SAndrew Baumann 
24897398d90SAndrew Baumann static const MemoryRegionOps bcm2835_aux_ops = {
24997398d90SAndrew Baumann     .read = bcm2835_aux_read,
25097398d90SAndrew Baumann     .write = bcm2835_aux_write,
25197398d90SAndrew Baumann     .endianness = DEVICE_NATIVE_ENDIAN,
25297398d90SAndrew Baumann     .valid.min_access_size = 4,
25397398d90SAndrew Baumann     .valid.max_access_size = 4,
25497398d90SAndrew Baumann };
25597398d90SAndrew Baumann 
25697398d90SAndrew Baumann static const VMStateDescription vmstate_bcm2835_aux = {
25797398d90SAndrew Baumann     .name = TYPE_BCM2835_AUX,
25897398d90SAndrew Baumann     .version_id = 1,
25997398d90SAndrew Baumann     .minimum_version_id = 1,
26097398d90SAndrew Baumann     .fields = (VMStateField[]) {
26197398d90SAndrew Baumann         VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
26297398d90SAndrew Baumann                             BCM2835_AUX_RX_FIFO_LEN),
26397398d90SAndrew Baumann         VMSTATE_UINT8(read_pos, BCM2835AuxState),
26497398d90SAndrew Baumann         VMSTATE_UINT8(read_count, BCM2835AuxState),
26597398d90SAndrew Baumann         VMSTATE_UINT8(ier, BCM2835AuxState),
26697398d90SAndrew Baumann         VMSTATE_UINT8(iir, BCM2835AuxState),
26797398d90SAndrew Baumann         VMSTATE_END_OF_LIST()
26897398d90SAndrew Baumann     }
26997398d90SAndrew Baumann };
27097398d90SAndrew Baumann 
27197398d90SAndrew Baumann static void bcm2835_aux_init(Object *obj)
27297398d90SAndrew Baumann {
27397398d90SAndrew Baumann     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
27497398d90SAndrew Baumann     BCM2835AuxState *s = BCM2835_AUX(obj);
27597398d90SAndrew Baumann 
27697398d90SAndrew Baumann     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
27797398d90SAndrew Baumann                           TYPE_BCM2835_AUX, 0x100);
27897398d90SAndrew Baumann     sysbus_init_mmio(sbd, &s->iomem);
27997398d90SAndrew Baumann     sysbus_init_irq(sbd, &s->irq);
28097398d90SAndrew Baumann }
28197398d90SAndrew Baumann 
28297398d90SAndrew Baumann static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
28397398d90SAndrew Baumann {
28497398d90SAndrew Baumann     BCM2835AuxState *s = BCM2835_AUX(dev);
28597398d90SAndrew Baumann 
2865345fdb4SMarc-André Lureau     qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
28781517ba3SAnton Nefedov                              bcm2835_aux_receive, NULL, NULL, s, NULL, true);
28897398d90SAndrew Baumann }
28997398d90SAndrew Baumann 
29097398d90SAndrew Baumann static Property bcm2835_aux_props[] = {
29197398d90SAndrew Baumann     DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
29297398d90SAndrew Baumann     DEFINE_PROP_END_OF_LIST(),
29397398d90SAndrew Baumann };
29497398d90SAndrew Baumann 
29597398d90SAndrew Baumann static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
29697398d90SAndrew Baumann {
29797398d90SAndrew Baumann     DeviceClass *dc = DEVICE_CLASS(oc);
29897398d90SAndrew Baumann 
29997398d90SAndrew Baumann     dc->realize = bcm2835_aux_realize;
30097398d90SAndrew Baumann     dc->vmsd = &vmstate_bcm2835_aux;
30197398d90SAndrew Baumann     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
302*4f67d30bSMarc-André Lureau     device_class_set_props(dc, bcm2835_aux_props);
30397398d90SAndrew Baumann }
30497398d90SAndrew Baumann 
30597398d90SAndrew Baumann static const TypeInfo bcm2835_aux_info = {
30697398d90SAndrew Baumann     .name          = TYPE_BCM2835_AUX,
30797398d90SAndrew Baumann     .parent        = TYPE_SYS_BUS_DEVICE,
30897398d90SAndrew Baumann     .instance_size = sizeof(BCM2835AuxState),
30997398d90SAndrew Baumann     .instance_init = bcm2835_aux_init,
31097398d90SAndrew Baumann     .class_init    = bcm2835_aux_class_init,
31197398d90SAndrew Baumann };
31297398d90SAndrew Baumann 
31397398d90SAndrew Baumann static void bcm2835_aux_register_types(void)
31497398d90SAndrew Baumann {
31597398d90SAndrew Baumann     type_register_static(&bcm2835_aux_info);
31697398d90SAndrew Baumann }
31797398d90SAndrew Baumann 
31897398d90SAndrew Baumann type_init(bcm2835_aux_register_types)
319