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) |
390 | |