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