]> git.proxmox.com Git - mirror_qemu.git/blob - hw/pl011.c
ARMv7 support.
[mirror_qemu.git] / hw / pl011.c
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 "vl.h"
11
12 typedef struct {
13 uint32_t base;
14 uint32_t readbuff;
15 uint32_t flags;
16 uint32_t lcr;
17 uint32_t cr;
18 uint32_t dmacr;
19 uint32_t int_enabled;
20 uint32_t int_level;
21 uint32_t read_fifo[16];
22 uint32_t ilpr;
23 uint32_t ibrd;
24 uint32_t fbrd;
25 uint32_t ifl;
26 int read_pos;
27 int read_count;
28 int read_trigger;
29 CharDriverState *chr;
30 qemu_irq irq;
31 enum pl011_type type;
32 } pl011_state;
33
34 #define PL011_INT_TX 0x20
35 #define PL011_INT_RX 0x10
36
37 #define PL011_FLAG_TXFE 0x80
38 #define PL011_FLAG_RXFF 0x40
39 #define PL011_FLAG_TXFF 0x20
40 #define PL011_FLAG_RXFE 0x10
41
42 static const unsigned char pl011_id[2][8] = {
43 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
44 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
45 };
46
47 static void pl011_update(pl011_state *s)
48 {
49 uint32_t flags;
50
51 flags = s->int_level & s->int_enabled;
52 qemu_set_irq(s->irq, flags != 0);
53 }
54
55 static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
56 {
57 pl011_state *s = (pl011_state *)opaque;
58 uint32_t c;
59
60 offset -= s->base;
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 return c;
80 case 1: /* UARTCR */
81 return 0;
82 case 6: /* UARTFR */
83 return s->flags;
84 case 8: /* UARTILPR */
85 return s->ilpr;
86 case 9: /* UARTIBRD */
87 return s->ibrd;
88 case 10: /* UARTFBRD */
89 return s->fbrd;
90 case 11: /* UARTLCR_H */
91 return s->lcr;
92 case 12: /* UARTCR */
93 return s->cr;
94 case 13: /* UARTIFLS */
95 return s->ifl;
96 case 14: /* UARTIMSC */
97 return s->int_enabled;
98 case 15: /* UARTRIS */
99 return s->int_level;
100 case 16: /* UARTMIS */
101 return s->int_level & s->int_enabled;
102 case 18: /* UARTDMACR */
103 return s->dmacr;
104 default:
105 cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
106 return 0;
107 }
108 }
109
110 static void pl011_set_read_trigger(pl011_state *s)
111 {
112 #if 0
113 /* The docs say the RX interrupt is triggered when the FIFO exceeds
114 the threshold. However linux only reads the FIFO in response to an
115 interrupt. Triggering the interrupt when the FIFO is non-empty seems
116 to make things work. */
117 if (s->lcr & 0x10)
118 s->read_trigger = (s->ifl >> 1) & 0x1c;
119 else
120 #endif
121 s->read_trigger = 1;
122 }
123
124 static void pl011_write(void *opaque, target_phys_addr_t offset,
125 uint32_t value)
126 {
127 pl011_state *s = (pl011_state *)opaque;
128 unsigned char ch;
129
130 offset -= s->base;
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_receive(void *opaque, const uint8_t *buf, int size)
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] = *buf;
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_event(void *opaque, int event)
216 {
217 /* ??? Should probably implement break. */
218 }
219
220 static CPUReadMemoryFunc *pl011_readfn[] = {
221 pl011_read,
222 pl011_read,
223 pl011_read
224 };
225
226 static CPUWriteMemoryFunc *pl011_writefn[] = {
227 pl011_write,
228 pl011_write,
229 pl011_write
230 };
231
232 void pl011_init(uint32_t base, qemu_irq irq,
233 CharDriverState *chr, enum pl011_type type)
234 {
235 int iomemtype;
236 pl011_state *s;
237
238 s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
239 iomemtype = cpu_register_io_memory(0, pl011_readfn,
240 pl011_writefn, s);
241 cpu_register_physical_memory(base, 0x00001000, iomemtype);
242 s->base = base;
243 s->irq = irq;
244 s->type = type;
245 s->chr = chr;
246 s->read_trigger = 1;
247 s->ifl = 0x12;
248 s->cr = 0x300;
249 s->flags = 0x90;
250 if (chr){
251 qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
252 pl011_event, s);
253 }
254 /* ??? Save/restore. */
255 }
256