Prepare for op.c removal and zero legacy ops
[qemu/mini2440.git] / hw / pl011.c
blob9d8c6a3f58670d5b2f1e7aee225a440864cd3517
1 /*
2 * Arm PrimeCell PL011 UART
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licenced under the GPL.
8 */
10 #include "hw.h"
11 #include "qemu-char.h"
12 #include "primecell.h"
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;
36 #define PL011_INT_TX 0x20
37 #define PL011_INT_RX 0x10
39 #define PL011_FLAG_TXFE 0x80
40 #define PL011_FLAG_RXFF 0x40
41 #define PL011_FLAG_TXFF 0x20
42 #define PL011_FLAG_RXFE 0x10
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 */
49 static void pl011_update(pl011_state *s)
51 uint32_t flags;
53 flags = s->int_level & s->int_enabled;
54 qemu_set_irq(s->irq, flags != 0);
57 static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
59 pl011_state *s = (pl011_state *)opaque;
60 uint32_t c;
62 offset -= s->base;
63 if (offset >= 0xfe0 && offset < 0x1000) {
64 return pl011_id[s->type][(offset - 0xfe0) >> 2];
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;
75 if (s->read_count == 0) {
76 s->flags |= PL011_FLAG_RXFE;
78 if (s->read_count == s->read_trigger - 1)
79 s->int_level &= ~ PL011_INT_RX;
80 pl011_update(s);
81 qemu_chr_accept_input(s->chr);
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:
108 cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
109 return 0;
113 static void pl011_set_read_trigger(pl011_state *s)
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;
127 static void pl011_write(void *opaque, target_phys_addr_t offset,
128 uint32_t value)
130 pl011_state *s = (pl011_state *)opaque;
131 unsigned char ch;
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;
146 case 6: /* UARTFR */
147 /* Writes to Flag register are ignored. */
148 break;
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:
184 cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
188 static int pl011_can_receive(void *opaque)
190 pl011_state *s = (pl011_state *)opaque;
192 if (s->lcr & 0x10)
193 return s->read_count < 16;
194 else
195 return s->read_count < 1;
198 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
200 pl011_state *s = (pl011_state *)opaque;
201 int slot;
203 slot = s->read_pos + s->read_count;
204 if (slot >= 16)
205 slot -= 16;
206 s->read_fifo[slot] = *buf;
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;
212 if (s->read_count == s->read_trigger) {
213 s->int_level |= PL011_INT_RX;
214 pl011_update(s);
218 static void pl011_event(void *opaque, int event)
220 /* ??? Should probably implement break. */
223 static CPUReadMemoryFunc *pl011_readfn[] = {
224 pl011_read,
225 pl011_read,
226 pl011_read
229 static CPUWriteMemoryFunc *pl011_writefn[] = {
230 pl011_write,
231 pl011_write,
232 pl011_write
235 void pl011_init(uint32_t base, qemu_irq irq,
236 CharDriverState *chr, enum pl011_type type)
238 int iomemtype;
239 pl011_state *s;
241 s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
242 iomemtype = cpu_register_io_memory(0, pl011_readfn,
243 pl011_writefn, s);
244 cpu_register_physical_memory(base, 0x00001000, iomemtype);
245 s->base = base;
246 s->irq = irq;
247 s->type = type;
248 s->chr = chr;
249 s->read_trigger = 1;
250 s->ifl = 0x12;
251 s->cr = 0x300;
252 s->flags = 0x90;
253 if (chr){
254 qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
255 pl011_event, s);
257 /* ??? Save/restore. */