xref: /openbmc/qemu/hw/char/pl011.c (revision f6bda9cb)
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 #include "hw/sysbus.h"
11 #include "sysemu/char.h"
12 
13 #define TYPE_PL011 "pl011"
14 #define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011)
15 
16 typedef struct PL011State {
17     SysBusDevice parent_obj;
18 
19     MemoryRegion iomem;
20     uint32_t readbuff;
21     uint32_t flags;
22     uint32_t lcr;
23     uint32_t cr;
24     uint32_t dmacr;
25     uint32_t int_enabled;
26     uint32_t int_level;
27     uint32_t read_fifo[16];
28     uint32_t ilpr;
29     uint32_t ibrd;
30     uint32_t fbrd;
31     uint32_t ifl;
32     int read_pos;
33     int read_count;
34     int read_trigger;
35     CharDriverState *chr;
36     qemu_irq irq;
37     const unsigned char *id;
38 } PL011State;
39 
40 #define PL011_INT_TX 0x20
41 #define PL011_INT_RX 0x10
42 
43 #define PL011_FLAG_TXFE 0x80
44 #define PL011_FLAG_RXFF 0x40
45 #define PL011_FLAG_TXFF 0x20
46 #define PL011_FLAG_RXFE 0x10
47 
48 static const unsigned char pl011_id_arm[8] =
49   { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
50 static const unsigned char pl011_id_luminary[8] =
51   { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
52 
53 static void pl011_update(PL011State *s)
54 {
55     uint32_t flags;
56 
57     flags = s->int_level & s->int_enabled;
58     qemu_set_irq(s->irq, flags != 0);
59 }
60 
61 static uint64_t pl011_read(void *opaque, hwaddr offset,
62                            unsigned size)
63 {
64     PL011State *s = (PL011State *)opaque;
65     uint32_t c;
66 
67     if (offset >= 0xfe0 && offset < 0x1000) {
68         return s->id[(offset - 0xfe0) >> 2];
69     }
70     switch (offset >> 2) {
71     case 0: /* UARTDR */
72         s->flags &= ~PL011_FLAG_RXFF;
73         c = s->read_fifo[s->read_pos];
74         if (s->read_count > 0) {
75             s->read_count--;
76             if (++s->read_pos == 16)
77                 s->read_pos = 0;
78         }
79         if (s->read_count == 0) {
80             s->flags |= PL011_FLAG_RXFE;
81         }
82         if (s->read_count == s->read_trigger - 1)
83             s->int_level &= ~ PL011_INT_RX;
84         pl011_update(s);
85         if (s->chr) {
86             qemu_chr_accept_input(s->chr);
87         }
88         return c;
89     case 1: /* UARTCR */
90         return 0;
91     case 6: /* UARTFR */
92         return s->flags;
93     case 8: /* UARTILPR */
94         return s->ilpr;
95     case 9: /* UARTIBRD */
96         return s->ibrd;
97     case 10: /* UARTFBRD */
98         return s->fbrd;
99     case 11: /* UARTLCR_H */
100         return s->lcr;
101     case 12: /* UARTCR */
102         return s->cr;
103     case 13: /* UARTIFLS */
104         return s->ifl;
105     case 14: /* UARTIMSC */
106         return s->int_enabled;
107     case 15: /* UARTRIS */
108         return s->int_level;
109     case 16: /* UARTMIS */
110         return s->int_level & s->int_enabled;
111     case 18: /* UARTDMACR */
112         return s->dmacr;
113     default:
114         qemu_log_mask(LOG_GUEST_ERROR,
115                       "pl011_read: Bad offset %x\n", (int)offset);
116         return 0;
117     }
118 }
119 
120 static void pl011_set_read_trigger(PL011State *s)
121 {
122 #if 0
123     /* The docs say the RX interrupt is triggered when the FIFO exceeds
124        the threshold.  However linux only reads the FIFO in response to an
125        interrupt.  Triggering the interrupt when the FIFO is non-empty seems
126        to make things work.  */
127     if (s->lcr & 0x10)
128         s->read_trigger = (s->ifl >> 1) & 0x1c;
129     else
130 #endif
131         s->read_trigger = 1;
132 }
133 
134 static void pl011_write(void *opaque, hwaddr offset,
135                         uint64_t value, unsigned size)
136 {
137     PL011State *s = (PL011State *)opaque;
138     unsigned char ch;
139 
140     switch (offset >> 2) {
141     case 0: /* UARTDR */
142         /* ??? Check if transmitter is enabled.  */
143         ch = value;
144         if (s->chr)
145             qemu_chr_fe_write(s->chr, &ch, 1);
146         s->int_level |= PL011_INT_TX;
147         pl011_update(s);
148         break;
149     case 1: /* UARTCR */
150         s->cr = value;
151         break;
152     case 6: /* UARTFR */
153         /* Writes to Flag register are ignored.  */
154         break;
155     case 8: /* UARTUARTILPR */
156         s->ilpr = value;
157         break;
158     case 9: /* UARTIBRD */
159         s->ibrd = value;
160         break;
161     case 10: /* UARTFBRD */
162         s->fbrd = value;
163         break;
164     case 11: /* UARTLCR_H */
165         s->lcr = value;
166         pl011_set_read_trigger(s);
167         break;
168     case 12: /* UARTCR */
169         /* ??? Need to implement the enable and loopback bits.  */
170         s->cr = value;
171         break;
172     case 13: /* UARTIFS */
173         s->ifl = value;
174         pl011_set_read_trigger(s);
175         break;
176     case 14: /* UARTIMSC */
177         s->int_enabled = value;
178         pl011_update(s);
179         break;
180     case 17: /* UARTICR */
181         s->int_level &= ~value;
182         pl011_update(s);
183         break;
184     case 18: /* UARTDMACR */
185         s->dmacr = value;
186         if (value & 3) {
187             qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
188         }
189         break;
190     default:
191         qemu_log_mask(LOG_GUEST_ERROR,
192                       "pl011_write: Bad offset %x\n", (int)offset);
193     }
194 }
195 
196 static int pl011_can_receive(void *opaque)
197 {
198     PL011State *s = (PL011State *)opaque;
199 
200     if (s->lcr & 0x10)
201         return s->read_count < 16;
202     else
203         return s->read_count < 1;
204 }
205 
206 static void pl011_put_fifo(void *opaque, uint32_t value)
207 {
208     PL011State *s = (PL011State *)opaque;
209     int slot;
210 
211     slot = s->read_pos + s->read_count;
212     if (slot >= 16)
213         slot -= 16;
214     s->read_fifo[slot] = value;
215     s->read_count++;
216     s->flags &= ~PL011_FLAG_RXFE;
217     if (s->cr & 0x10 || s->read_count == 16) {
218         s->flags |= PL011_FLAG_RXFF;
219     }
220     if (s->read_count == s->read_trigger) {
221         s->int_level |= PL011_INT_RX;
222         pl011_update(s);
223     }
224 }
225 
226 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
227 {
228     pl011_put_fifo(opaque, *buf);
229 }
230 
231 static void pl011_event(void *opaque, int event)
232 {
233     if (event == CHR_EVENT_BREAK)
234         pl011_put_fifo(opaque, 0x400);
235 }
236 
237 static const MemoryRegionOps pl011_ops = {
238     .read = pl011_read,
239     .write = pl011_write,
240     .endianness = DEVICE_NATIVE_ENDIAN,
241 };
242 
243 static const VMStateDescription vmstate_pl011 = {
244     .name = "pl011",
245     .version_id = 1,
246     .minimum_version_id = 1,
247     .minimum_version_id_old = 1,
248     .fields      = (VMStateField[]) {
249         VMSTATE_UINT32(readbuff, PL011State),
250         VMSTATE_UINT32(flags, PL011State),
251         VMSTATE_UINT32(lcr, PL011State),
252         VMSTATE_UINT32(cr, PL011State),
253         VMSTATE_UINT32(dmacr, PL011State),
254         VMSTATE_UINT32(int_enabled, PL011State),
255         VMSTATE_UINT32(int_level, PL011State),
256         VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16),
257         VMSTATE_UINT32(ilpr, PL011State),
258         VMSTATE_UINT32(ibrd, PL011State),
259         VMSTATE_UINT32(fbrd, PL011State),
260         VMSTATE_UINT32(ifl, PL011State),
261         VMSTATE_INT32(read_pos, PL011State),
262         VMSTATE_INT32(read_count, PL011State),
263         VMSTATE_INT32(read_trigger, PL011State),
264         VMSTATE_END_OF_LIST()
265     }
266 };
267 
268 static void pl011_init(Object *obj)
269 {
270     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
271     PL011State *s = PL011(obj);
272 
273     memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
274     sysbus_init_mmio(sbd, &s->iomem);
275     sysbus_init_irq(sbd, &s->irq);
276 
277     s->read_trigger = 1;
278     s->ifl = 0x12;
279     s->cr = 0x300;
280     s->flags = 0x90;
281 
282     s->id = pl011_id_arm;
283 }
284 
285 static void pl011_realize(DeviceState *dev, Error **errp)
286 {
287     PL011State *s = PL011(dev);
288 
289     s->chr = qemu_char_get_next_serial();
290 
291     if (s->chr) {
292         qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
293                               pl011_event, s);
294     }
295 }
296 
297 static void pl011_class_init(ObjectClass *oc, void *data)
298 {
299     DeviceClass *dc = DEVICE_CLASS(oc);
300 
301     dc->realize = pl011_realize;
302     dc->vmsd = &vmstate_pl011;
303 }
304 
305 static const TypeInfo pl011_arm_info = {
306     .name          = TYPE_PL011,
307     .parent        = TYPE_SYS_BUS_DEVICE,
308     .instance_size = sizeof(PL011State),
309     .instance_init = pl011_init,
310     .class_init    = pl011_class_init,
311 };
312 
313 static void pl011_luminary_init(Object *obj)
314 {
315     PL011State *s = PL011(obj);
316 
317     s->id = pl011_id_luminary;
318 }
319 
320 static const TypeInfo pl011_luminary_info = {
321     .name          = "pl011_luminary",
322     .parent        = TYPE_PL011,
323     .instance_init = pl011_luminary_init,
324 };
325 
326 static void pl011_register_types(void)
327 {
328     type_register_static(&pl011_arm_info);
329     type_register_static(&pl011_luminary_info);
330 }
331 
332 type_init(pl011_register_types)
333