]> git.proxmox.com Git - mirror_qemu.git/blame - hw/pl011.c
Add basic OMAP2 chip support.
[mirror_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);
bd9bdce6 81 qemu_chr_accept_input(s->chr);
cdbdb648
PB
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:
4d1165fa 108 cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
cdbdb648
PB
109 return 0;
110 }
111}
112
113static void pl011_set_read_trigger(pl011_state *s)
114{
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;
125}
126
127static void pl011_write(void *opaque, target_phys_addr_t offset,
128 uint32_t value)
129{
130 pl011_state *s = (pl011_state *)opaque;
131 unsigned char ch;
132
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;
9ee6e8bb
PB
146 case 6: /* UARTFR */
147 /* Writes to Flag register are ignored. */
148 break;
cdbdb648
PB
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:
4d1165fa 184 cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
cdbdb648
PB
185 }
186}
187
aa1f17c1 188static int pl011_can_receive(void *opaque)
cdbdb648
PB
189{
190 pl011_state *s = (pl011_state *)opaque;
191
192 if (s->lcr & 0x10)
193 return s->read_count < 16;
194 else
195 return s->read_count < 1;
196}
197
cc9c9ffc 198static void pl011_put_fifo(void *opaque, uint32_t value)
cdbdb648
PB
199{
200 pl011_state *s = (pl011_state *)opaque;
201 int slot;
202
203 slot = s->read_pos + s->read_count;
204 if (slot >= 16)
205 slot -= 16;
cc9c9ffc 206 s->read_fifo[slot] = value;
cdbdb648
PB
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;
211 }
212 if (s->read_count == s->read_trigger) {
213 s->int_level |= PL011_INT_RX;
214 pl011_update(s);
215 }
216}
217
cc9c9ffc
AJ
218static void pl011_receive(void *opaque, const uint8_t *buf, int size)
219{
220 pl011_put_fifo(opaque, *buf);
221}
222
cdbdb648
PB
223static void pl011_event(void *opaque, int event)
224{
cc9c9ffc
AJ
225 if (event == CHR_EVENT_BREAK)
226 pl011_put_fifo(opaque, 0x400);
cdbdb648
PB
227}
228
229static CPUReadMemoryFunc *pl011_readfn[] = {
230 pl011_read,
231 pl011_read,
232 pl011_read
233};
234
235static CPUWriteMemoryFunc *pl011_writefn[] = {
236 pl011_write,
237 pl011_write,
238 pl011_write
239};
240
d537cf6c 241void pl011_init(uint32_t base, qemu_irq irq,
9ee6e8bb 242 CharDriverState *chr, enum pl011_type type)
cdbdb648
PB
243{
244 int iomemtype;
245 pl011_state *s;
246
247 s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
248 iomemtype = cpu_register_io_memory(0, pl011_readfn,
249 pl011_writefn, s);
187337f8 250 cpu_register_physical_memory(base, 0x00001000, iomemtype);
cdbdb648 251 s->base = base;
cdbdb648 252 s->irq = irq;
9ee6e8bb 253 s->type = type;
cdbdb648
PB
254 s->chr = chr;
255 s->read_trigger = 1;
256 s->ifl = 0x12;
257 s->cr = 0x300;
258 s->flags = 0x90;
5fafdf24 259 if (chr){
aa1f17c1 260 qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
e5b0bc44 261 pl011_event, s);
cdbdb648
PB
262 }
263 /* ??? Save/restore. */
264}
265