]> git.proxmox.com Git - qemu.git/blob - hw/char/pl011.c
sysemu: avoid proliferation of include/ subdirectories
[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 #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)