increase max kernel size with initrd
[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 "vl.h"
11
12 typedef 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
42 static const unsigned char pl011_id[] =
43 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
44
45 static 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
53 static 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
108 static 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
122 static 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
180 static 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
190 static 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
210 static void pl011_event(void *opaque, int event)
211 {
212     /* ??? Should probably implement break.  */
213 }
214
215 static CPUReadMemoryFunc *pl011_readfn[] = {
216    pl011_read,
217    pl011_read,
218    pl011_read
219 };
220
221 static CPUWriteMemoryFunc *pl011_writefn[] = {
222    pl011_write,
223    pl011_write,
224    pl011_write
225 };
226
227 void 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