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