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