bcm2835_aux: s/Bcm2835AuxState/BCM2835AuxState/ for consistency
[qemu/ar7.git] / hw / char / bcm2835_aux.c
blob3c27a7b0df68002b1c8f27d7b4ed9362de201d8d
1 /*
2 * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
3 * Copyright (c) 2015, Microsoft
4 * Written by Andrew Baumann
6 * Fairly hacky. Based on gutted code for pl011 emulation (copyright below).
7 */
9 /*
10 * Arm PrimeCell PL011 UART
12 * Copyright (c) 2006 CodeSourcery.
13 * Written by Paul Brook
15 * This code is licensed under the GPL.
18 #include "hw/char/bcm2835_aux.h"
20 static void bcm2835_aux_update(BCM2835AuxState *s)
22 bool status = (s->rx_int_enable && s->read_count != 0) || s->tx_int_enable;
23 qemu_set_irq(s->irq, status);
26 static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset,
27 unsigned size)
29 BCM2835AuxState *s = (BCM2835AuxState *)opaque;
30 uint32_t c, res;
32 switch (offset >> 2) {
33 case 1: /* AUXENB */
34 return 1; /* mini UART enabled */
36 case 16: /* AUX_MU_IO_REG */
37 c = s->read_fifo[s->read_pos];
38 if (s->read_count > 0) {
39 s->read_count--;
40 if (++s->read_pos == 8) {
41 s->read_pos = 0;
44 if (s->chr) {
45 qemu_chr_accept_input(s->chr);
47 bcm2835_aux_update(s);
48 return c;
50 case 17: /* AUX_MU_IIR_REG */
51 res = 0;
52 if (s->rx_int_enable) {
53 res |= 0x2;
55 if (s->tx_int_enable) {
56 res |= 0x1;
58 return res;
60 case 18: /* AUX_MU_IER_REG */
61 res = 0xc0;
62 if (s->tx_int_enable) {
63 res |= 0x1;
64 } else if (s->rx_int_enable && s->read_count != 0) {
65 res |= 0x2;
67 return res;
69 case 21: /* AUX_MU_LSR_REG */
70 res = 0x60; /* tx idle, empty */
71 if (s->read_count != 0) {
72 res |= 0x1;
74 return res;
76 case 25: /* AUX_MU_STAT_REG */
77 res = 0x302; /* space in the output buffer, empty tx fifo */
78 if (s->read_count > 0) {
79 res |= 0x1; /* data in input buffer */
80 assert(s->read_count < 8);
81 res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
83 return res;
85 default:
86 qemu_log_mask(LOG_GUEST_ERROR,
87 "bcm2835_aux_read: Bad offset %x\n", (int)offset);
88 return 0;
92 static void bcm2835_aux_write(void *opaque, hwaddr offset,
93 uint64_t value, unsigned size)
95 BCM2835AuxState *s = (BCM2835AuxState *)opaque;
96 unsigned char ch;
98 switch (offset >> 2) {
99 case 1: /* AUXENB */
100 if (value != 1) {
101 qemu_log_mask(LOG_GUEST_ERROR,
102 "bcm2835_aux_write: Trying to enable SPI or"
103 " disable UART. Not supported!\n");
105 break;
107 case 16: /* AUX_MU_IO_REG */
108 ch = value;
109 if (s->chr) {
110 qemu_chr_fe_write(s->chr, &ch, 1);
112 break;
114 case 17: /* AUX_MU_IIR_REG */
115 s->rx_int_enable = (value & 0x2) != 0;
116 s->tx_int_enable = (value & 0x1) != 0;
117 break;
119 case 18: /* AUX_MU_IER_REG */
120 if (value & 0x1) {
121 s->read_count = 0;
123 break;
125 default:
126 qemu_log_mask(LOG_GUEST_ERROR,
127 "bcm2835_aux_write: Bad offset %x\n", (int)offset);
130 bcm2835_aux_update(s);
133 static int bcm2835_aux_can_receive(void *opaque)
135 BCM2835AuxState *s = (BCM2835AuxState *)opaque;
137 return s->read_count < 8;
140 static void bcm2835_aux_put_fifo(void *opaque, uint32_t value)
142 BCM2835AuxState *s = (BCM2835AuxState *)opaque;
143 int slot;
145 slot = s->read_pos + s->read_count;
146 if (slot >= 8) {
147 slot -= 8;
149 s->read_fifo[slot] = value;
150 s->read_count++;
151 if (s->read_count == 8) {
152 /* buffer full */
154 bcm2835_aux_update(s);
157 static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
159 bcm2835_aux_put_fifo(opaque, *buf);
162 static void bcm2835_aux_event(void *opaque, int event)
164 if (event == CHR_EVENT_BREAK) {
165 bcm2835_aux_put_fifo(opaque, 0x400);
169 static const MemoryRegionOps bcm2835_aux_ops = {
170 .read = bcm2835_aux_read,
171 .write = bcm2835_aux_write,
172 .endianness = DEVICE_NATIVE_ENDIAN,
175 static const VMStateDescription vmstate_bcm2835_aux = {
176 .name = "bcm2835_aux",
177 .version_id = 2,
178 .minimum_version_id = 2,
179 .fields = (VMStateField[]) {
180 VMSTATE_UINT32_ARRAY(read_fifo, BCM2835AuxState, 8),
181 VMSTATE_INT32(read_pos, BCM2835AuxState),
182 VMSTATE_INT32(read_count, BCM2835AuxState),
183 VMSTATE_END_OF_LIST()
187 static void bcm2835_aux_init(Object *obj)
189 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
190 BCM2835AuxState *s = BCM2835_AUX(obj);
192 memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
193 "bcm2835_aux", 0x100);
194 sysbus_init_mmio(sbd, &s->iomem);
195 sysbus_init_irq(sbd, &s->irq);
198 static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
200 BCM2835AuxState *s = BCM2835_AUX(dev);
202 /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
203 s->chr = qemu_char_get_next_serial();
205 if (s->chr) {
206 qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
207 bcm2835_aux_receive, bcm2835_aux_event, s);
211 static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
213 DeviceClass *dc = DEVICE_CLASS(oc);
215 dc->realize = bcm2835_aux_realize;
216 dc->vmsd = &vmstate_bcm2835_aux;
217 /* Reason: realize() method uses qemu_char_get_next_serial() */
218 dc->cannot_instantiate_with_device_add_yet = true;
221 static const TypeInfo bcm2835_aux_info = {
222 .name = TYPE_BCM2835_AUX,
223 .parent = TYPE_SYS_BUS_DEVICE,
224 .instance_size = sizeof(BCM2835AuxState),
225 .instance_init = bcm2835_aux_init,
226 .class_init = bcm2835_aux_class_init,
229 static void bcm2835_aux_register_types(void)
231 type_register_static(&bcm2835_aux_info);
234 type_init(bcm2835_aux_register_types)