xref: /openbmc/qemu/hw/char/bcm2835_aux.c (revision 650d103d3ea959212f826acb9d3fe80cf30e347b)
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 "migration/vmstate.h"
27 #include "qemu/log.h"
28 #include "qemu/module.h"
29 
30 #define AUX_IRQ         0x0
31 #define AUX_ENABLES     0x4
32 #define AUX_MU_IO_REG   0x40
33 #define AUX_MU_IER_REG  0x44
34 #define AUX_MU_IIR_REG  0x48
35 #define AUX_MU_LCR_REG  0x4c
36 #define AUX_MU_MCR_REG  0x50
37 #define AUX_MU_LSR_REG  0x54
38 #define AUX_MU_MSR_REG  0x58
39 #define AUX_MU_SCRATCH  0x5c
40 #define AUX_MU_CNTL_REG 0x60
41 #define AUX_MU_STAT_REG 0x64
42 #define AUX_MU_BAUD_REG 0x68
43 
44 /* bits in IER/IIR registers */
45 #define RX_INT  0x1
46 #define TX_INT  0x2
47 
48 static void bcm2835_aux_update(BCM2835AuxState *s)
49 {
50     /* signal an interrupt if either:
51      * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
52      * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
53      */
54     s->iir = 0;
55     if ((s->ier & RX_INT) && s->read_count != 0) {
56         s->iir |= RX_INT;
57     }
58     if (s->ier & TX_INT) {
59         s->iir |= TX_INT;
60     }
61     qemu_set_irq(s->irq, s->iir != 0);
62 }
63 
64 static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
65 {
66     BCM2835AuxState *s = opaque;
67     uint32_t c, res;
68 
69     switch (offset) {
70     case AUX_IRQ:
71         return s->iir != 0;
72 
73     case AUX_ENABLES:
74         return 1; /* mini UART permanently enabled */
75 
76     case AUX_MU_IO_REG:
77         /* "DLAB bit set means access baudrate register" is NYI */
78         c = s->read_fifo[s->read_pos];
79         if (s->read_count > 0) {
80             s->read_count--;
81             if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
82                 s->read_pos = 0;
83             }
84         }
85         qemu_chr_fe_accept_input(&s->chr);
86         bcm2835_aux_update(s);
87         return c;
88 
89     case AUX_MU_IER_REG:
90         /* "DLAB bit set means access baudrate register" is NYI */
91         return 0xc0 | s->ier; /* FIFO enables always read 1 */
92 
93     case AUX_MU_IIR_REG:
94         res = 0xc0; /* FIFO enables */
95         /* The spec is unclear on what happens when both tx and rx
96          * interrupts are active, besides that this cannot occur. At
97          * present, we choose to prioritise the rx interrupt, since
98          * the tx fifo is always empty. */
99         if (s->read_count != 0) {
100             res |= 0x4;
101         } else {
102             res |= 0x2;
103         }
104         if (s->iir == 0) {
105             res |= 0x1;
106         }
107         return res;
108 
109     case AUX_MU_LCR_REG:
110         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
111         return 0;
112 
113     case AUX_MU_MCR_REG:
114         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
115         return 0;
116 
117     case AUX_MU_LSR_REG:
118         res = 0x60; /* tx idle, empty */
119         if (s->read_count != 0) {
120             res |= 0x1;
121         }
122         return res;
123 
124     case AUX_MU_MSR_REG:
125         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
126         return 0;
127 
128     case AUX_MU_SCRATCH:
129         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
130         return 0;
131 
132     case AUX_MU_CNTL_REG:
133         return 0x3; /* tx, rx enabled */
134 
135     case AUX_MU_STAT_REG:
136         res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
137         if (s->read_count > 0) {
138             res |= 0x1; /* data in input buffer */
139             assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
140             res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
141         }
142         return res;
143 
144     case AUX_MU_BAUD_REG:
145         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
146         return 0;
147 
148     default:
149         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
150                       __func__, offset);
151         return 0;
152     }
153 }
154 
155 static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
156                               unsigned size)
157 {
158     BCM2835AuxState *s = opaque;
159     unsigned char ch;
160 
161     switch (offset) {
162     case AUX_ENABLES:
163         if (value != 1) {
164             qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
165                           "or disable UART\n", __func__);
166         }
167         break;
168 
169     case AUX_MU_IO_REG:
170         /* "DLAB bit set means access baudrate register" is NYI */
171         ch = value;
172         /* XXX this blocks entire thread. Rewrite to use
173          * qemu_chr_fe_write and background I/O callbacks */
174         qemu_chr_fe_write_all(&s->chr, &ch, 1);
175         break;
176 
177     case AUX_MU_IER_REG:
178         /* "DLAB bit set means access baudrate register" is NYI */
179         s->ier = value & (TX_INT | RX_INT);
180         bcm2835_aux_update(s);
181         break;
182 
183     case AUX_MU_IIR_REG:
184         if (value & 0x2) {
185             s->read_count = 0;
186         }
187         break;
188 
189     case AUX_MU_LCR_REG:
190         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
191         break;
192 
193     case AUX_MU_MCR_REG:
194         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
195         break;
196 
197     case AUX_MU_SCRATCH:
198         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
199         break;
200 
201     case AUX_MU_CNTL_REG:
202         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
203         break;
204 
205     case AUX_MU_BAUD_REG:
206         qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
207         break;
208 
209     default:
210         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
211                       __func__, offset);
212     }
213 
214     bcm2835_aux_update(s);
215 }
216 
217 static int bcm2835_aux_can_receive(void *opaque)
218 {
219     BCM2835AuxState *s = opaque;
220 
221     return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
222 }
223 
224 static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
225 {
226     BCM2835AuxState *s = opaque;
227     int slot;
228 
229     slot = s->read_pos + s->read_count;
230     if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
231         slot -= BCM2835_AUX_RX_FIFO_LEN;
232     }
233     s->read_fifo[slot] = value;
234     s->read_count++;
235     if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
236         /* buffer full */
237     }
238     bcm2835_aux_update(s);
239 }
240 
241 static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
242 {
243     bcm2835_aux_put_fifo(opaque, *buf);
244 }
245 
246 static const MemoryRegionOps bcm2835_aux_ops = {
247     .read = bcm2835_aux_read,
248     .write = bcm2835_aux_write,
249     .endianness = DEVICE_NATIVE_ENDIAN,
250     .valid.min_access_size = 4,
251     .valid.max_access_size = 4,
252 };
253 
254 static const VMStateDescription vmstate_bcm2835_aux = {
255     .name = TYPE_BCM2835_AUX,
256     .version_id = 1,
257     .minimum_version_id = 1,
258     .fields = (VMStateField[]) {
259         VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
260                             BCM2835_AUX_RX_FIFO_LEN),
261         VMSTATE_UINT8(read_pos, BCM2835AuxState),
262         VMSTATE_UINT8(read_count, BCM2835AuxState),
263         VMSTATE_UINT8(ier, BCM2835AuxState),
264         VMSTATE_UINT8(iir, BCM2835AuxState),
265         VMSTATE_END_OF_LIST()
266     }
267 };
268 
269 static void bcm2835_aux_init(Object *obj)
270 {
271     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
272     BCM2835AuxState *s = BCM2835_AUX(obj);
273 
274     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
275                           TYPE_BCM2835_AUX, 0x100);
276     sysbus_init_mmio(sbd, &s->iomem);
277     sysbus_init_irq(sbd, &s->irq);
278 }
279 
280 static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
281 {
282     BCM2835AuxState *s = BCM2835_AUX(dev);
283 
284     qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
285                              bcm2835_aux_receive, NULL, NULL, s, NULL, true);
286 }
287 
288 static Property bcm2835_aux_props[] = {
289     DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
290     DEFINE_PROP_END_OF_LIST(),
291 };
292 
293 static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
294 {
295     DeviceClass *dc = DEVICE_CLASS(oc);
296 
297     dc->realize = bcm2835_aux_realize;
298     dc->vmsd = &vmstate_bcm2835_aux;
299     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
300     dc->props = bcm2835_aux_props;
301 }
302 
303 static const TypeInfo bcm2835_aux_info = {
304     .name          = TYPE_BCM2835_AUX,
305     .parent        = TYPE_SYS_BUS_DEVICE,
306     .instance_size = sizeof(BCM2835AuxState),
307     .instance_init = bcm2835_aux_init,
308     .class_init    = bcm2835_aux_class_init,
309 };
310 
311 static void bcm2835_aux_register_types(void)
312 {
313     type_register_static(&bcm2835_aux_info);
314 }
315 
316 type_init(bcm2835_aux_register_types)
317