Break up vl.h.
[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 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;
32     qemu_irq irq;
33     enum pl011_type type;
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
44 static 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 };
48
49 static void pl011_update(pl011_state *s)
50 {
51     uint32_t flags;
52
53     flags = s->int_level & s->int_enabled;
54     qemu_set_irq(s->irq, flags != 0);
55 }
56
57 static 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) {
64         return pl011_id[s->type][(offset - 0xfe0) >> 2];
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:
107         cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
108         return 0;
109     }
110 }
111
112 static 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
126 static 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;
145     case 6: /* UARTFR */
146         /* Writes to Flag register are ignored.  */
147         break;
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:
183         cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
184     }
185 }
186
187 static int pl011_can_receive(void *opaque)
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
197 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
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
217 static void pl011_event(void *opaque, int event)
218 {
219     /* ??? Should probably implement break.  */
220 }
221
222 static CPUReadMemoryFunc *pl011_readfn[] = {
223    pl011_read,
224    pl011_read,
225    pl011_read
226 };
227
228 static CPUWriteMemoryFunc *pl011_writefn[] = {
229    pl011_write,
230    pl011_write,
231    pl011_write
232 };
233
234 void pl011_init(uint32_t base, qemu_irq irq,
235                 CharDriverState *chr, enum pl011_type type)
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);
243     cpu_register_physical_memory(base, 0x00001000, iomemtype);
244     s->base = base;
245     s->irq = irq;
246     s->type = type;
247     s->chr = chr;
248     s->read_trigger = 1;
249     s->ifl = 0x12;
250     s->cr = 0x300;
251     s->flags = 0x90;
252     if (chr){
253         qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
254                               pl011_event, s);
255     }
256     /* ??? Save/restore.  */
257 }
258