2 * Arm PrimeCell PL011 UART
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licenced under the GPL.
11 #include "qemu-char.h"
12 #include "primecell.h"
23 uint32_t read_fifo
[16];
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
)
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
;
63 if (offset
>= 0xfe0 && offset
< 0x1000) {
64 return pl011_id
[s
->type
][(offset
- 0xfe0) >> 2];
66 switch (offset
>> 2) {
68 s
->flags
&= ~PL011_FLAG_RXFF
;
69 c
= s
->read_fifo
[s
->read_pos
];
70 if (s
->read_count
> 0) {
72 if (++s
->read_pos
== 16)
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
;
81 qemu_chr_accept_input(s
->chr
);
87 case 8: /* UARTILPR */
89 case 9: /* UARTIBRD */
91 case 10: /* UARTFBRD */
93 case 11: /* UARTLCR_H */
97 case 13: /* UARTIFLS */
99 case 14: /* UARTIMSC */
100 return s
->int_enabled
;
101 case 15: /* UARTRIS */
103 case 16: /* UARTMIS */
104 return s
->int_level
& s
->int_enabled
;
105 case 18: /* UARTDMACR */
108 cpu_abort (cpu_single_env
, "pl011_read: Bad offset %x\n", (int)offset
);
113 static void pl011_set_read_trigger(pl011_state
*s
)
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. */
121 s
->read_trigger
= (s
->ifl
>> 1) & 0x1c;
127 static void pl011_write(void *opaque
, target_phys_addr_t offset
,
130 pl011_state
*s
= (pl011_state
*)opaque
;
134 switch (offset
>> 2) {
136 /* ??? Check if transmitter is enabled. */
139 qemu_chr_write(s
->chr
, &ch
, 1);
140 s
->int_level
|= PL011_INT_TX
;
147 /* Writes to Flag register are ignored. */
149 case 8: /* UARTUARTILPR */
152 case 9: /* UARTIBRD */
155 case 10: /* UARTFBRD */
158 case 11: /* UARTLCR_H */
160 pl011_set_read_trigger(s
);
162 case 12: /* UARTCR */
163 /* ??? Need to implement the enable and loopback bits. */
166 case 13: /* UARTIFS */
168 pl011_set_read_trigger(s
);
170 case 14: /* UARTIMSC */
171 s
->int_enabled
= value
;
174 case 17: /* UARTICR */
175 s
->int_level
&= ~value
;
178 case 18: /* UARTDMACR */
181 cpu_abort(cpu_single_env
, "PL011: DMA not implemented\n");
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
;
193 return s
->read_count
< 16;
195 return s
->read_count
< 1;
198 static void pl011_put_fifo(void *opaque
, uint32_t value
)
200 pl011_state
*s
= (pl011_state
*)opaque
;
203 slot
= s
->read_pos
+ s
->read_count
;
206 s
->read_fifo
[slot
] = value
;
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
;
218 static void pl011_receive(void *opaque
, const uint8_t *buf
, int size
)
220 pl011_put_fifo(opaque
, *buf
);
223 static void pl011_event(void *opaque
, int event
)
225 if (event
== CHR_EVENT_BREAK
)
226 pl011_put_fifo(opaque
, 0x400);
229 static CPUReadMemoryFunc
*pl011_readfn
[] = {
235 static CPUWriteMemoryFunc
*pl011_writefn
[] = {
241 void pl011_init(uint32_t base
, qemu_irq irq
,
242 CharDriverState
*chr
, enum pl011_type type
)
247 s
= (pl011_state
*)qemu_mallocz(sizeof(pl011_state
));
248 iomemtype
= cpu_register_io_memory(0, pl011_readfn
,
250 cpu_register_physical_memory(base
, 0x00001000, iomemtype
);
260 qemu_chr_add_handlers(chr
, pl011_can_receive
, pl011_receive
,
263 /* ??? Save/restore. */