]> Git Repo - qemu.git/blob - hw/pl011.c
hw/usb-net.c: Fix precedence bug when checking rndis_state
[qemu.git] / 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 licensed under the GPL.
8  */
9
10 #include "sysbus.h"
11 #include "qemu-char.h"
12
13 typedef struct {
14     SysBusDevice busdev;
15     MemoryRegion iomem;
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     const unsigned char *id;
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_arm[8] =
45   { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
46 static const unsigned char pl011_id_luminary[8] =
47   { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
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 uint64_t pl011_read(void *opaque, target_phys_addr_t offset,
58                            unsigned size)
59 {
60     pl011_state *s = (pl011_state *)opaque;
61     uint32_t c;
62
63     if (offset >= 0xfe0 && offset < 0x1000) {
64         return s->id[(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         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         hw_error("pl011_read: Bad offset %x\n", (int)offset);
109         return 0;
110     }
111 }
112
113 static 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
127 static void pl011_write(void *opaque, target_phys_addr_t offset,
128                         uint64_t value, unsigned size)
129 {
130     pl011_state *s = (pl011_state *)opaque;
131     unsigned char ch;
132
133     switch (offset >> 2) {
134     case 0: /* UARTDR */
135         /* ??? Check if transmitter is enabled.  */
136         ch = value;
137         if (s->chr)
138             qemu_chr_fe_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             hw_error("PL011: DMA not implemented\n");
181         break;
182     default:
183         hw_error("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_put_fifo(void *opaque, uint32_t value)
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] = value;
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_receive(void *opaque, const uint8_t *buf, int size)
218 {
219     pl011_put_fifo(opaque, *buf);
220 }
221
222 static void pl011_event(void *opaque, int event)
223 {
224     if (event == CHR_EVENT_BREAK)
225         pl011_put_fifo(opaque, 0x400);
226 }
227
228 static const MemoryRegionOps pl011_ops = {
229     .read = pl011_read,
230     .write = pl011_write,
231     .endianness = DEVICE_NATIVE_ENDIAN,
232 };
233
234 static const VMStateDescription vmstate_pl011 = {
235     .name = "pl011",
236     .version_id = 1,
237     .minimum_version_id = 1,
238     .minimum_version_id_old = 1,
239     .fields      = (VMStateField[]) {
240         VMSTATE_UINT32(readbuff, pl011_state),
241         VMSTATE_UINT32(flags, pl011_state),
242         VMSTATE_UINT32(lcr, pl011_state),
243         VMSTATE_UINT32(cr, pl011_state),
244         VMSTATE_UINT32(dmacr, pl011_state),
245         VMSTATE_UINT32(int_enabled, pl011_state),
246         VMSTATE_UINT32(int_level, pl011_state),
247         VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
248         VMSTATE_UINT32(ilpr, pl011_state),
249         VMSTATE_UINT32(ibrd, pl011_state),
250         VMSTATE_UINT32(fbrd, pl011_state),
251         VMSTATE_UINT32(ifl, pl011_state),
252         VMSTATE_INT32(read_pos, pl011_state),
253         VMSTATE_INT32(read_count, pl011_state),
254         VMSTATE_INT32(read_trigger, pl011_state),
255         VMSTATE_END_OF_LIST()
256     }
257 };
258
259 static int pl011_init(SysBusDevice *dev, const unsigned char *id)
260 {
261     pl011_state *s = FROM_SYSBUS(pl011_state, dev);
262
263     memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
264     sysbus_init_mmio(dev, &s->iomem);
265     sysbus_init_irq(dev, &s->irq);
266     s->id = id;
267     s->chr = qdev_init_chardev(&dev->qdev);
268
269     s->read_trigger = 1;
270     s->ifl = 0x12;
271     s->cr = 0x300;
272     s->flags = 0x90;
273     if (s->chr) {
274         qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
275                               pl011_event, s);
276     }
277     vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
278     return 0;
279 }
280
281 static int pl011_init_arm(SysBusDevice *dev)
282 {
283     return pl011_init(dev, pl011_id_arm);
284 }
285
286 static int pl011_init_luminary(SysBusDevice *dev)
287 {
288     return pl011_init(dev, pl011_id_luminary);
289 }
290
291 static void pl011_register_devices(void)
292 {
293     sysbus_register_dev("pl011", sizeof(pl011_state),
294                         pl011_init_arm);
295     sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
296                         pl011_init_luminary);
297 }
298
299 device_init(pl011_register_devices)
This page took 0.041368 seconds and 4 git commands to generate.