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