qdev scsi bus infrastructure
[qemu] / 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 "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         hw_error("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             hw_error("PL011: DMA not implemented\n");
179         break;
180     default:
181         hw_error("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 }