Fix tswap size
[qemu/mini2440.git] / hw / pl011.c
blobbbef0a4c6d9ae985168c9e137ab9f0567591908b
1 /*
2 * Arm PrimeCell PL011 UART
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licenced under the GPL.
8 */
10 #include "hw.h"
11 #include "qemu-char.h"
12 #include "primecell.h"
14 typedef struct {
15 uint32_t base;
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 enum pl011_type type;
34 } pl011_state;
36 #define PL011_INT_TX 0x20
37 #define PL011_INT_RX 0x10
39 #define PL011_FLAG_TXFE 0x80
40 #define PL011_FLAG_RXFF 0x40
41 #define PL011_FLAG_TXFF 0x20
42 #define PL011_FLAG_RXFE 0x10
44 static const unsigned char pl011_id[2][8] = {
45 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
46 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
49 static void pl011_update(pl011_state *s)
51 uint32_t flags;
53 flags = s->int_level & s->int_enabled;
54 qemu_set_irq(s->irq, flags != 0);
57 static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
59 pl011_state *s = (pl011_state *)opaque;
60 uint32_t c;
62 offset -= s->base;
63 if (offset >= 0xfe0 && offset < 0x1000) {
64 return pl011_id[s->type][(offset - 0xfe0) >> 2];
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;
75 if (s->read_count == 0) {
76 s->flags |= PL011_FLAG_RXFE;
78 if (s->read_count == s->read_trigger - 1)
79 s->int_level &= ~ PL011_INT_RX;
80 pl011_update(s);
81 qemu_chr_accept_input(s->chr);
82 return c;
83 case 1: /* UARTCR */
84 return 0;
85 case 6: /* UARTFR */
86 return s->flags;
87 case 8: /* UARTILPR */
88 return s->ilpr;
89 case 9: /* UARTIBRD */
90 return s->ibrd;
91 case 10: /* UARTFBRD */
92 return s->fbrd;
93 case 11: /* UARTLCR_H */
94 return s->lcr;
95 case 12: /* UARTCR */
96 return s->cr;
97 case 13: /* UARTIFLS */
98 return s->ifl;
99 case 14: /* UARTIMSC */
100 return s->int_enabled;
101 case 15: /* UARTRIS */
102 return s->int_level;
103 case 16: /* UARTMIS */
104 return s->int_level & s->int_enabled;
105 case 18: /* UARTDMACR */
106 return s->dmacr;
107 default:
108 cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
109 return 0;
113 static void pl011_set_read_trigger(pl011_state *s)
115 #if 0
116 /* The docs say the RX interrupt is triggered when the FIFO exceeds
117 the threshold. However linux only reads the FIFO in response to an
118 interrupt. Triggering the interrupt when the FIFO is non-empty seems
119 to make things work. */
120 if (s->lcr & 0x10)
121 s->read_trigger = (s->ifl >> 1) & 0x1c;
122 else
123 #endif
124 s->read_trigger = 1;
127 static void pl011_write(void *opaque, target_phys_addr_t offset,
128 uint32_t value)
130 pl011_state *s = (pl011_state *)opaque;
131 unsigned char ch;
133 offset -= s->base;
134 switch (offset >> 2) {
135 case 0: /* UARTDR */
136 /* ??? Check if transmitter is enabled. */
137 ch = value;
138 if (s->chr)
139 qemu_chr_write(s->chr, &ch, 1);
140 s->int_level |= PL011_INT_TX;
141 pl011_update(s);
142 break;
143 case 1: /* UARTCR */
144 s->cr = value;
145 break;
146 case 6: /* UARTFR */
147 /* Writes to Flag register are ignored. */
148 break;
149 case 8: /* UARTUARTILPR */
150 s->ilpr = value;
151 break;
152 case 9: /* UARTIBRD */
153 s->ibrd = value;
154 break;
155 case 10: /* UARTFBRD */
156 s->fbrd = value;
157 break;
158 case 11: /* UARTLCR_H */
159 s->lcr = value;
160 pl011_set_read_trigger(s);
161 break;
162 case 12: /* UARTCR */
163 /* ??? Need to implement the enable and loopback bits. */
164 s->cr = value;
165 break;
166 case 13: /* UARTIFS */
167 s->ifl = value;
168 pl011_set_read_trigger(s);
169 break;
170 case 14: /* UARTIMSC */
171 s->int_enabled = value;
172 pl011_update(s);
173 break;
174 case 17: /* UARTICR */
175 s->int_level &= ~value;
176 pl011_update(s);
177 break;
178 case 18: /* UARTDMACR */
179 s->dmacr = value;
180 if (value & 3)
181 cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
182 break;
183 default:
184 cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
188 static int pl011_can_receive(void *opaque)
190 pl011_state *s = (pl011_state *)opaque;
192 if (s->lcr & 0x10)
193 return s->read_count < 16;
194 else
195 return s->read_count < 1;
198 static void pl011_put_fifo(void *opaque, uint32_t value)
200 pl011_state *s = (pl011_state *)opaque;
201 int slot;
203 slot = s->read_pos + s->read_count;
204 if (slot >= 16)
205 slot -= 16;
206 s->read_fifo[slot] = value;
207 s->read_count++;
208 s->flags &= ~PL011_FLAG_RXFE;
209 if (s->cr & 0x10 || s->read_count == 16) {
210 s->flags |= PL011_FLAG_RXFF;
212 if (s->read_count == s->read_trigger) {
213 s->int_level |= PL011_INT_RX;
214 pl011_update(s);
218 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
220 pl011_put_fifo(opaque, *buf);
223 static void pl011_event(void *opaque, int event)
225 if (event == CHR_EVENT_BREAK)
226 pl011_put_fifo(opaque, 0x400);
229 static CPUReadMemoryFunc *pl011_readfn[] = {
230 pl011_read,
231 pl011_read,
232 pl011_read
235 static CPUWriteMemoryFunc *pl011_writefn[] = {
236 pl011_write,
237 pl011_write,
238 pl011_write
241 static void pl011_save(QEMUFile *f, void *opaque)
243 pl011_state *s = (pl011_state *)opaque;
244 int i;
246 qemu_put_be32(f, s->readbuff);
247 qemu_put_be32(f, s->flags);
248 qemu_put_be32(f, s->lcr);
249 qemu_put_be32(f, s->cr);
250 qemu_put_be32(f, s->dmacr);
251 qemu_put_be32(f, s->int_enabled);
252 qemu_put_be32(f, s->int_level);
253 for (i = 0; i < 16; i++)
254 qemu_put_be32(f, s->read_fifo[i]);
255 qemu_put_be32(f, s->ilpr);
256 qemu_put_be32(f, s->ibrd);
257 qemu_put_be32(f, s->fbrd);
258 qemu_put_be32(f, s->ifl);
259 qemu_put_be32(f, s->read_pos);
260 qemu_put_be32(f, s->read_count);
261 qemu_put_be32(f, s->read_trigger);
264 static int pl011_load(QEMUFile *f, void *opaque, int version_id)
266 pl011_state *s = (pl011_state *)opaque;
267 int i;
269 if (version_id != 1)
270 return -EINVAL;
272 s->readbuff = qemu_get_be32(f);
273 s->flags = qemu_get_be32(f);
274 s->lcr = qemu_get_be32(f);
275 s->cr = qemu_get_be32(f);
276 s->dmacr = qemu_get_be32(f);
277 s->int_enabled = qemu_get_be32(f);
278 s->int_level = qemu_get_be32(f);
279 for (i = 0; i < 16; i++)
280 s->read_fifo[i] = qemu_get_be32(f);
281 s->ilpr = qemu_get_be32(f);
282 s->ibrd = qemu_get_be32(f);
283 s->fbrd = qemu_get_be32(f);
284 s->ifl = qemu_get_be32(f);
285 s->read_pos = qemu_get_be32(f);
286 s->read_count = qemu_get_be32(f);
287 s->read_trigger = qemu_get_be32(f);
289 return 0;
292 void pl011_init(uint32_t base, qemu_irq irq,
293 CharDriverState *chr, enum pl011_type type)
295 int iomemtype;
296 pl011_state *s;
298 s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
299 iomemtype = cpu_register_io_memory(0, pl011_readfn,
300 pl011_writefn, s);
301 cpu_register_physical_memory(base, 0x00001000, iomemtype);
302 s->base = base;
303 s->irq = irq;
304 s->type = type;
305 s->chr = chr;
306 s->read_trigger = 1;
307 s->ifl = 0x12;
308 s->cr = 0x300;
309 s->flags = 0x90;
310 if (chr){
311 qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
312 pl011_event, s);
314 register_savevm("pl011_uart", -1, 1, pl011_save, pl011_load, s);