]> Git Repo - qemu.git/blob - hw/char/pl011.c
x86: move SMM property to X86MachineState
[qemu.git] / hw / char / pl011.c
1 /*
2  * Arm PrimeCell PL011 UART
3  *
4  * Copyright (c) 2006 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GPL.
8  */
9
10 /*
11  * QEMU interface:
12  *  + sysbus MMIO region 0: device registers
13  *  + sysbus IRQ 0: UARTINTR (combined interrupt line)
14  *  + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line)
15  *  + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line)
16  *  + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line)
17  *  + sysbus IRQ 4: UARTMSINTR (momem status interrupt line)
18  *  + sysbus IRQ 5: UARTEINTR (error interrupt line)
19  */
20
21 #include "qemu/osdep.h"
22 #include "hw/char/pl011.h"
23 #include "hw/irq.h"
24 #include "hw/sysbus.h"
25 #include "migration/vmstate.h"
26 #include "chardev/char-fe.h"
27 #include "qemu/log.h"
28 #include "qemu/module.h"
29 #include "trace.h"
30
31 #define PL011_INT_TX 0x20
32 #define PL011_INT_RX 0x10
33
34 #define PL011_FLAG_TXFE 0x80
35 #define PL011_FLAG_RXFF 0x40
36 #define PL011_FLAG_TXFF 0x20
37 #define PL011_FLAG_RXFE 0x10
38
39 /* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
40 #define INT_OE (1 << 10)
41 #define INT_BE (1 << 9)
42 #define INT_PE (1 << 8)
43 #define INT_FE (1 << 7)
44 #define INT_RT (1 << 6)
45 #define INT_TX (1 << 5)
46 #define INT_RX (1 << 4)
47 #define INT_DSR (1 << 3)
48 #define INT_DCD (1 << 2)
49 #define INT_CTS (1 << 1)
50 #define INT_RI (1 << 0)
51 #define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
52 #define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
53
54 static const unsigned char pl011_id_arm[8] =
55   { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
56 static const unsigned char pl011_id_luminary[8] =
57   { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
58
59 /* Which bits in the interrupt status matter for each outbound IRQ line ? */
60 static const uint32_t irqmask[] = {
61     INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
62     INT_RX,
63     INT_TX,
64     INT_RT,
65     INT_MS,
66     INT_E,
67 };
68
69 static void pl011_update(PL011State *s)
70 {
71     uint32_t flags;
72     int i;
73
74     flags = s->int_level & s->int_enabled;
75     trace_pl011_irq_state(flags != 0);
76     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
77         qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
78     }
79 }
80
81 static uint64_t pl011_read(void *opaque, hwaddr offset,
82                            unsigned size)
83 {
84     PL011State *s = (PL011State *)opaque;
85     uint32_t c;
86     uint64_t r;
87
88     switch (offset >> 2) {
89     case 0: /* UARTDR */
90         s->flags &= ~PL011_FLAG_RXFF;
91         c = s->read_fifo[s->read_pos];
92         if (s->read_count > 0) {
93             s->read_count--;
94             if (++s->read_pos == 16)
95                 s->read_pos = 0;
96         }
97         if (s->read_count == 0) {
98             s->flags |= PL011_FLAG_RXFE;
99         }
100         if (s->read_count == s->read_trigger - 1)
101             s->int_level &= ~ PL011_INT_RX;
102         trace_pl011_read_fifo(s->read_count);
103         s->rsr = c >> 8;
104         pl011_update(s);
105         qemu_chr_fe_accept_input(&s->chr);
106         r = c;
107         break;
108     case 1: /* UARTRSR */
109         r = s->rsr;
110         break;
111     case 6: /* UARTFR */
112         r = s->flags;
113         break;
114     case 8: /* UARTILPR */
115         r = s->ilpr;
116         break;
117     case 9: /* UARTIBRD */
118         r = s->ibrd;
119         break;
120     case 10: /* UARTFBRD */
121         r = s->fbrd;
122         break;
123     case 11: /* UARTLCR_H */
124         r = s->lcr;
125         break;
126     case 12: /* UARTCR */
127         r = s->cr;
128         break;
129     case 13: /* UARTIFLS */
130         r = s->ifl;
131         break;
132     case 14: /* UARTIMSC */
133         r = s->int_enabled;
134         break;
135     case 15: /* UARTRIS */
136         r = s->int_level;
137         break;
138     case 16: /* UARTMIS */
139         r = s->int_level & s->int_enabled;
140         break;
141     case 18: /* UARTDMACR */
142         r = s->dmacr;
143         break;
144     case 0x3f8 ... 0x400:
145         r = s->id[(offset - 0xfe0) >> 2];
146         break;
147     default:
148         qemu_log_mask(LOG_GUEST_ERROR,
149                       "pl011_read: Bad offset 0x%x\n", (int)offset);
150         r = 0;
151         break;
152     }
153
154     trace_pl011_read(offset, r);
155     return r;
156 }
157
158 static void pl011_set_read_trigger(PL011State *s)
159 {
160 #if 0
161     /* The docs say the RX interrupt is triggered when the FIFO exceeds
162        the threshold.  However linux only reads the FIFO in response to an
163        interrupt.  Triggering the interrupt when the FIFO is non-empty seems
164        to make things work.  */
165     if (s->lcr & 0x10)
166         s->read_trigger = (s->ifl >> 1) & 0x1c;
167     else
168 #endif
169         s->read_trigger = 1;
170 }
171
172 static void pl011_write(void *opaque, hwaddr offset,
173                         uint64_t value, unsigned size)
174 {
175     PL011State *s = (PL011State *)opaque;
176     unsigned char ch;
177
178     trace_pl011_write(offset, value);
179
180     switch (offset >> 2) {
181     case 0: /* UARTDR */
182         /* ??? Check if transmitter is enabled.  */
183         ch = value;
184         /* XXX this blocks entire thread. Rewrite to use
185          * qemu_chr_fe_write and background I/O callbacks */
186         qemu_chr_fe_write_all(&s->chr, &ch, 1);
187         s->int_level |= PL011_INT_TX;
188         pl011_update(s);
189         break;
190     case 1: /* UARTRSR/UARTECR */
191         s->rsr = 0;
192         break;
193     case 6: /* UARTFR */
194         /* Writes to Flag register are ignored.  */
195         break;
196     case 8: /* UARTUARTILPR */
197         s->ilpr = value;
198         break;
199     case 9: /* UARTIBRD */
200         s->ibrd = value;
201         break;
202     case 10: /* UARTFBRD */
203         s->fbrd = value;
204         break;
205     case 11: /* UARTLCR_H */
206         /* Reset the FIFO state on FIFO enable or disable */
207         if ((s->lcr ^ value) & 0x10) {
208             s->read_count = 0;
209             s->read_pos = 0;
210         }
211         s->lcr = value;
212         pl011_set_read_trigger(s);
213         break;
214     case 12: /* UARTCR */
215         /* ??? Need to implement the enable and loopback bits.  */
216         s->cr = value;
217         break;
218     case 13: /* UARTIFS */
219         s->ifl = value;
220         pl011_set_read_trigger(s);
221         break;
222     case 14: /* UARTIMSC */
223         s->int_enabled = value;
224         pl011_update(s);
225         break;
226     case 17: /* UARTICR */
227         s->int_level &= ~value;
228         pl011_update(s);
229         break;
230     case 18: /* UARTDMACR */
231         s->dmacr = value;
232         if (value & 3) {
233             qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
234         }
235         break;
236     default:
237         qemu_log_mask(LOG_GUEST_ERROR,
238                       "pl011_write: Bad offset 0x%x\n", (int)offset);
239     }
240 }
241
242 static int pl011_can_receive(void *opaque)
243 {
244     PL011State *s = (PL011State *)opaque;
245     int r;
246
247     if (s->lcr & 0x10) {
248         r = s->read_count < 16;
249     } else {
250         r = s->read_count < 1;
251     }
252     trace_pl011_can_receive(s->lcr, s->read_count, r);
253     return r;
254 }
255
256 static void pl011_put_fifo(void *opaque, uint32_t value)
257 {
258     PL011State *s = (PL011State *)opaque;
259     int slot;
260
261     slot = s->read_pos + s->read_count;
262     if (slot >= 16)
263         slot -= 16;
264     s->read_fifo[slot] = value;
265     s->read_count++;
266     s->flags &= ~PL011_FLAG_RXFE;
267     trace_pl011_put_fifo(value, s->read_count);
268     if (!(s->lcr & 0x10) || s->read_count == 16) {
269         trace_pl011_put_fifo_full();
270         s->flags |= PL011_FLAG_RXFF;
271     }
272     if (s->read_count == s->read_trigger) {
273         s->int_level |= PL011_INT_RX;
274         pl011_update(s);
275     }
276 }
277
278 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
279 {
280     pl011_put_fifo(opaque, *buf);
281 }
282
283 static void pl011_event(void *opaque, int event)
284 {
285     if (event == CHR_EVENT_BREAK)
286         pl011_put_fifo(opaque, 0x400);
287 }
288
289 static const MemoryRegionOps pl011_ops = {
290     .read = pl011_read,
291     .write = pl011_write,
292     .endianness = DEVICE_NATIVE_ENDIAN,
293 };
294
295 static const VMStateDescription vmstate_pl011 = {
296     .name = "pl011",
297     .version_id = 2,
298     .minimum_version_id = 2,
299     .fields = (VMStateField[]) {
300         VMSTATE_UINT32(readbuff, PL011State),
301         VMSTATE_UINT32(flags, PL011State),
302         VMSTATE_UINT32(lcr, PL011State),
303         VMSTATE_UINT32(rsr, PL011State),
304         VMSTATE_UINT32(cr, PL011State),
305         VMSTATE_UINT32(dmacr, PL011State),
306         VMSTATE_UINT32(int_enabled, PL011State),
307         VMSTATE_UINT32(int_level, PL011State),
308         VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16),
309         VMSTATE_UINT32(ilpr, PL011State),
310         VMSTATE_UINT32(ibrd, PL011State),
311         VMSTATE_UINT32(fbrd, PL011State),
312         VMSTATE_UINT32(ifl, PL011State),
313         VMSTATE_INT32(read_pos, PL011State),
314         VMSTATE_INT32(read_count, PL011State),
315         VMSTATE_INT32(read_trigger, PL011State),
316         VMSTATE_END_OF_LIST()
317     }
318 };
319
320 static Property pl011_properties[] = {
321     DEFINE_PROP_CHR("chardev", PL011State, chr),
322     DEFINE_PROP_END_OF_LIST(),
323 };
324
325 static void pl011_init(Object *obj)
326 {
327     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
328     PL011State *s = PL011(obj);
329     int i;
330
331     memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
332     sysbus_init_mmio(sbd, &s->iomem);
333     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
334         sysbus_init_irq(sbd, &s->irq[i]);
335     }
336
337     s->read_trigger = 1;
338     s->ifl = 0x12;
339     s->cr = 0x300;
340     s->flags = 0x90;
341
342     s->id = pl011_id_arm;
343 }
344
345 static void pl011_realize(DeviceState *dev, Error **errp)
346 {
347     PL011State *s = PL011(dev);
348
349     qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
350                              pl011_event, NULL, s, NULL, true);
351 }
352
353 static void pl011_class_init(ObjectClass *oc, void *data)
354 {
355     DeviceClass *dc = DEVICE_CLASS(oc);
356
357     dc->realize = pl011_realize;
358     dc->vmsd = &vmstate_pl011;
359     dc->props = pl011_properties;
360 }
361
362 static const TypeInfo pl011_arm_info = {
363     .name          = TYPE_PL011,
364     .parent        = TYPE_SYS_BUS_DEVICE,
365     .instance_size = sizeof(PL011State),
366     .instance_init = pl011_init,
367     .class_init    = pl011_class_init,
368 };
369
370 static void pl011_luminary_init(Object *obj)
371 {
372     PL011State *s = PL011(obj);
373
374     s->id = pl011_id_luminary;
375 }
376
377 static const TypeInfo pl011_luminary_info = {
378     .name          = TYPE_PL011_LUMINARY,
379     .parent        = TYPE_PL011,
380     .instance_init = pl011_luminary_init,
381 };
382
383 static void pl011_register_types(void)
384 {
385     type_register_static(&pl011_arm_info);
386     type_register_static(&pl011_luminary_info);
387 }
388
389 type_init(pl011_register_types)
This page took 0.043698 seconds and 4 git commands to generate.