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