]> git.proxmox.com Git - mirror_qemu.git/blame - hw/pl011.c
ARM Versatile Platform Baseboard emulation.
[mirror_qemu.git] / hw / pl011.c
CommitLineData
cdbdb648
PB
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
12typedef 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 void *pic;
31 int irq;
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
42static const unsigned char pl011_id[] =
43{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
44
45static void pl011_update(pl011_state *s)
46{
47 uint32_t flags;
48
49 flags = s->int_level & s->int_enabled;
50 pic_set_irq_new(s->pic, s->irq, flags != 0);
51}
52
53static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
54{
55 pl011_state *s = (pl011_state *)opaque;
56 uint32_t c;
57
58 offset -= s->base;
59 if (offset >= 0xfe0 && offset < 0x1000) {
60 return pl011_id[(offset - 0xfe0) >> 2];
61 }
62 switch (offset >> 2) {
63 case 0: /* UARTDR */
64 s->flags &= ~PL011_FLAG_RXFF;
65 c = s->read_fifo[s->read_pos];
66 if (s->read_count > 0) {
67 s->read_count--;
68 if (++s->read_pos == 16)
69 s->read_pos = 0;
70 }
71 if (s->read_count == 0) {
72 s->flags |= PL011_FLAG_RXFE;
73 }
74 if (s->read_count == s->read_trigger - 1)
75 s->int_level &= ~ PL011_INT_RX;
76 pl011_update(s);
77 return c;
78 case 1: /* UARTCR */
79 return 0;
80 case 6: /* UARTFR */
81 return s->flags;
82 case 8: /* UARTILPR */
83 return s->ilpr;
84 case 9: /* UARTIBRD */
85 return s->ibrd;
86 case 10: /* UARTFBRD */
87 return s->fbrd;
88 case 11: /* UARTLCR_H */
89 return s->lcr;
90 case 12: /* UARTCR */
91 return s->cr;
92 case 13: /* UARTIFLS */
93 return s->ifl;
94 case 14: /* UARTIMSC */
95 return s->int_enabled;
96 case 15: /* UARTRIS */
97 return s->int_level;
98 case 16: /* UARTMIS */
99 return s->int_level & s->int_enabled;
100 case 18: /* UARTDMACR */
101 return s->dmacr;
102 default:
103 cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
104 return 0;
105 }
106}
107
108static void pl011_set_read_trigger(pl011_state *s)
109{
110#if 0
111 /* The docs say the RX interrupt is triggered when the FIFO exceeds
112 the threshold. However linux only reads the FIFO in response to an
113 interrupt. Triggering the interrupt when the FIFO is non-empty seems
114 to make things work. */
115 if (s->lcr & 0x10)
116 s->read_trigger = (s->ifl >> 1) & 0x1c;
117 else
118#endif
119 s->read_trigger = 1;
120}
121
122static void pl011_write(void *opaque, target_phys_addr_t offset,
123 uint32_t value)
124{
125 pl011_state *s = (pl011_state *)opaque;
126 unsigned char ch;
127
128 offset -= s->base;
129 switch (offset >> 2) {
130 case 0: /* UARTDR */
131 /* ??? Check if transmitter is enabled. */
132 ch = value;
133 if (s->chr)
134 qemu_chr_write(s->chr, &ch, 1);
135 s->int_level |= PL011_INT_TX;
136 pl011_update(s);
137 break;
138 case 1: /* UARTCR */
139 s->cr = value;
140 break;
141 case 8: /* UARTUARTILPR */
142 s->ilpr = value;
143 break;
144 case 9: /* UARTIBRD */
145 s->ibrd = value;
146 break;
147 case 10: /* UARTFBRD */
148 s->fbrd = value;
149 break;
150 case 11: /* UARTLCR_H */
151 s->lcr = value;
152 pl011_set_read_trigger(s);
153 break;
154 case 12: /* UARTCR */
155 /* ??? Need to implement the enable and loopback bits. */
156 s->cr = value;
157 break;
158 case 13: /* UARTIFS */
159 s->ifl = value;
160 pl011_set_read_trigger(s);
161 break;
162 case 14: /* UARTIMSC */
163 s->int_enabled = value;
164 pl011_update(s);
165 break;
166 case 17: /* UARTICR */
167 s->int_level &= ~value;
168 pl011_update(s);
169 break;
170 case 18: /* UARTDMACR */
171 s->dmacr = value;
172 if (value & 3)
173 cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
174 break;
175 default:
176 cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
177 }
178}
179
180static int pl011_can_recieve(void *opaque)
181{
182 pl011_state *s = (pl011_state *)opaque;
183
184 if (s->lcr & 0x10)
185 return s->read_count < 16;
186 else
187 return s->read_count < 1;
188}
189
190static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
191{
192 pl011_state *s = (pl011_state *)opaque;
193 int slot;
194
195 slot = s->read_pos + s->read_count;
196 if (slot >= 16)
197 slot -= 16;
198 s->read_fifo[slot] = *buf;
199 s->read_count++;
200 s->flags &= ~PL011_FLAG_RXFE;
201 if (s->cr & 0x10 || s->read_count == 16) {
202 s->flags |= PL011_FLAG_RXFF;
203 }
204 if (s->read_count == s->read_trigger) {
205 s->int_level |= PL011_INT_RX;
206 pl011_update(s);
207 }
208}
209
210static void pl011_event(void *opaque, int event)
211{
212 /* ??? Should probably implement break. */
213}
214
215static CPUReadMemoryFunc *pl011_readfn[] = {
216 pl011_read,
217 pl011_read,
218 pl011_read
219};
220
221static CPUWriteMemoryFunc *pl011_writefn[] = {
222 pl011_write,
223 pl011_write,
224 pl011_write
225};
226
227void pl011_init(uint32_t base, void *pic, int irq,
228 CharDriverState *chr)
229{
230 int iomemtype;
231 pl011_state *s;
232
233 s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
234 iomemtype = cpu_register_io_memory(0, pl011_readfn,
235 pl011_writefn, s);
236 cpu_register_physical_memory(base, 0x00000fff, iomemtype);
237 s->base = base;
238 s->pic = pic;
239 s->irq = irq;
240 s->chr = chr;
241 s->read_trigger = 1;
242 s->ifl = 0x12;
243 s->cr = 0x300;
244 s->flags = 0x90;
245 if (chr){
246 qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s);
247 qemu_chr_add_event_handler(chr, pl011_event);
248 }
249 /* ??? Save/restore. */
250}
251