qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

bcm2835_aux.c (9063B)


      1 /*
      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:
      6  *
      7  * Arm PrimeCell PL011 UART
      8  *
      9  * Copyright (c) 2006 CodeSourcery.
     10  * Written by Paul Brook
     11  *
     12  * This code is licensed under the GPL.
     13  *
     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
     17  *  - Scratch register
     18  *  - Extra control
     19  *  - Baudrate
     20  *  - SPI interfaces
     21  */
     22 
     23 #include "qemu/osdep.h"
     24 #include "hw/char/bcm2835_aux.h"
     25 #include "hw/irq.h"
     26 #include "hw/qdev-properties.h"
     27 #include "hw/qdev-properties-system.h"
     28 #include "migration/vmstate.h"
     29 #include "qemu/log.h"
     30 #include "qemu/module.h"
     31 
     32 #define AUX_IRQ         0x0
     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
     45 
     46 /* bits in IER/IIR registers */
     47 #define RX_INT  0x1
     48 #define TX_INT  0x2
     49 
     50 static void bcm2835_aux_update(BCM2835AuxState *s)
     51 {
     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)
     55      */
     56     s->iir = 0;
     57     if ((s->ier & RX_INT) && s->read_count != 0) {
     58         s->iir |= RX_INT;
     59     }
     60     if (s->ier & TX_INT) {
     61         s->iir |= TX_INT;
     62     }
     63     qemu_set_irq(s->irq, s->iir != 0);
     64 }
     65 
     66 static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
     67 {
     68     BCM2835AuxState *s = opaque;
     69     uint32_t c, res;
     70 
     71     switch (offset) {
     72     case AUX_IRQ:
     73         return s->iir != 0;
     74 
     75     case AUX_ENABLES:
     76         return 1; /* mini UART permanently enabled */
     77 
     78     case AUX_MU_IO_REG:
     79         /* "DLAB bit set means access baudrate register" is NYI */
     80         c = s->read_fifo[s->read_pos];
     81         if (s->read_count > 0) {
     82             s->read_count--;
     83             if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
     84                 s->read_pos = 0;
     85             }
     86         }
     87         qemu_chr_fe_accept_input(&s->chr);
     88         bcm2835_aux_update(s);
     89         return c;
     90 
     91     case AUX_MU_IER_REG:
     92         /* "DLAB bit set means access baudrate register" is NYI */
     93         return 0xc0 | s->ier; /* FIFO enables always read 1 */
     94 
     95     case AUX_MU_IIR_REG:
     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) {
    102             res |= 0x4;
    103         } else {
    104             res |= 0x2;
    105         }
    106         if (s->iir == 0) {
    107             res |= 0x1;
    108         }
    109         return res;
    110 
    111     case AUX_MU_LCR_REG:
    112         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
    113         return 0;
    114 
    115     case AUX_MU_MCR_REG:
    116         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
    117         return 0;
    118 
    119     case AUX_MU_LSR_REG:
    120         res = 0x60; /* tx idle, empty */
    121         if (s->read_count != 0) {
    122             res |= 0x1;
    123         }
    124         return res;
    125 
    126     case AUX_MU_MSR_REG:
    127         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
    128         return 0;
    129 
    130     case AUX_MU_SCRATCH:
    131         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
    132         return 0;
    133 
    134     case AUX_MU_CNTL_REG:
    135         return 0x3; /* tx, rx enabled */
    136 
    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 */
    143         }
    144         return res;
    145 
    146     case AUX_MU_BAUD_REG:
    147         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
    148         return 0;
    149 
    150     default:
    151         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    152                       __func__, offset);
    153         return 0;
    154     }
    155 }
    156 
    157 static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
    158                               unsigned size)
    159 {
    160     BCM2835AuxState *s = opaque;
    161     unsigned char ch;
    162 
    163     switch (offset) {
    164     case AUX_ENABLES:
    165         if (value != 1) {
    166             qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI"
    167                                      " or disable UART: 0x%"PRIx64"\n",
    168                           __func__, value);
    169         }
    170         break;
    171 
    172     case AUX_MU_IO_REG:
    173         /* "DLAB bit set means access baudrate register" is NYI */
    174         ch = value;
    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);
    178         break;
    179 
    180     case AUX_MU_IER_REG:
    181         /* "DLAB bit set means access baudrate register" is NYI */
    182         s->ier = value & (TX_INT | RX_INT);
    183         bcm2835_aux_update(s);
    184         break;
    185 
    186     case AUX_MU_IIR_REG:
    187         if (value & 0x2) {
    188             s->read_count = 0;
    189         }
    190         break;
    191 
    192     case AUX_MU_LCR_REG:
    193         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
    194         break;
    195 
    196     case AUX_MU_MCR_REG:
    197         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
    198         break;
    199 
    200     case AUX_MU_SCRATCH:
    201         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
    202         break;
    203 
    204     case AUX_MU_CNTL_REG:
    205         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
    206         break;
    207 
    208     case AUX_MU_BAUD_REG:
    209         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
    210         break;
    211 
    212     default:
    213         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    214                       __func__, offset);
    215     }
    216 
    217     bcm2835_aux_update(s);
    218 }
    219 
    220 static int bcm2835_aux_can_receive(void *opaque)
    221 {
    222     BCM2835AuxState *s = opaque;
    223 
    224     return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
    225 }
    226 
    227 static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
    228 {
    229     BCM2835AuxState *s = opaque;
    230     int slot;
    231 
    232     slot = s->read_pos + s->read_count;
    233     if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
    234         slot -= BCM2835_AUX_RX_FIFO_LEN;
    235     }
    236     s->read_fifo[slot] = value;
    237     s->read_count++;
    238     if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
    239         /* buffer full */
    240     }
    241     bcm2835_aux_update(s);
    242 }
    243 
    244 static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
    245 {
    246     bcm2835_aux_put_fifo(opaque, *buf);
    247 }
    248 
    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,
    257 };
    258 
    259 static const VMStateDescription vmstate_bcm2835_aux = {
    260     .name = TYPE_BCM2835_AUX,
    261     .version_id = 1,
    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()
    271     }
    272 };
    273 
    274 static void bcm2835_aux_init(Object *obj)
    275 {
    276     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    277     BCM2835AuxState *s = BCM2835_AUX(obj);
    278 
    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);
    283 }
    284 
    285 static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
    286 {
    287     BCM2835AuxState *s = BCM2835_AUX(dev);
    288 
    289     qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
    290                              bcm2835_aux_receive, NULL, NULL, s, NULL, true);
    291 }
    292 
    293 static Property bcm2835_aux_props[] = {
    294     DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
    295     DEFINE_PROP_END_OF_LIST(),
    296 };
    297 
    298 static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
    299 {
    300     DeviceClass *dc = DEVICE_CLASS(oc);
    301 
    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);
    306 }
    307 
    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,
    314 };
    315 
    316 static void bcm2835_aux_register_types(void)
    317 {
    318     type_register_static(&bcm2835_aux_info);
    319 }
    320 
    321 type_init(bcm2835_aux_register_types)