root / hw / char / mcf_uart.c @ dccfcd0e
History | View | Annotate | Download (7.1 kB)
1 |
/*
|
---|---|
2 |
* ColdFire UART emulation.
|
3 |
*
|
4 |
* Copyright (c) 2007 CodeSourcery.
|
5 |
*
|
6 |
* This code is licensed under the GPL
|
7 |
*/
|
8 |
#include "hw/hw.h" |
9 |
#include "hw/m68k/mcf.h" |
10 |
#include "sysemu/char.h" |
11 |
#include "exec/address-spaces.h" |
12 |
|
13 |
typedef struct { |
14 |
MemoryRegion iomem; |
15 |
uint8_t mr[2];
|
16 |
uint8_t sr; |
17 |
uint8_t isr; |
18 |
uint8_t imr; |
19 |
uint8_t bg1; |
20 |
uint8_t bg2; |
21 |
uint8_t fifo[4];
|
22 |
uint8_t tb; |
23 |
int current_mr;
|
24 |
int fifo_len;
|
25 |
int tx_enabled;
|
26 |
int rx_enabled;
|
27 |
qemu_irq irq; |
28 |
CharDriverState *chr; |
29 |
} mcf_uart_state; |
30 |
|
31 |
/* UART Status Register bits. */
|
32 |
#define MCF_UART_RxRDY 0x01 |
33 |
#define MCF_UART_FFULL 0x02 |
34 |
#define MCF_UART_TxRDY 0x04 |
35 |
#define MCF_UART_TxEMP 0x08 |
36 |
#define MCF_UART_OE 0x10 |
37 |
#define MCF_UART_PE 0x20 |
38 |
#define MCF_UART_FE 0x40 |
39 |
#define MCF_UART_RB 0x80 |
40 |
|
41 |
/* Interrupt flags. */
|
42 |
#define MCF_UART_TxINT 0x01 |
43 |
#define MCF_UART_RxINT 0x02 |
44 |
#define MCF_UART_DBINT 0x04 |
45 |
#define MCF_UART_COSINT 0x80 |
46 |
|
47 |
/* UMR1 flags. */
|
48 |
#define MCF_UART_BC0 0x01 |
49 |
#define MCF_UART_BC1 0x02 |
50 |
#define MCF_UART_PT 0x04 |
51 |
#define MCF_UART_PM0 0x08 |
52 |
#define MCF_UART_PM1 0x10 |
53 |
#define MCF_UART_ERR 0x20 |
54 |
#define MCF_UART_RxIRQ 0x40 |
55 |
#define MCF_UART_RxRTS 0x80 |
56 |
|
57 |
static void mcf_uart_update(mcf_uart_state *s) |
58 |
{ |
59 |
s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT); |
60 |
if (s->sr & MCF_UART_TxRDY)
|
61 |
s->isr |= MCF_UART_TxINT; |
62 |
if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ) |
63 |
? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0)
|
64 |
s->isr |= MCF_UART_RxINT; |
65 |
|
66 |
qemu_set_irq(s->irq, (s->isr & s->imr) != 0);
|
67 |
} |
68 |
|
69 |
uint64_t mcf_uart_read(void *opaque, hwaddr addr,
|
70 |
unsigned size)
|
71 |
{ |
72 |
mcf_uart_state *s = (mcf_uart_state *)opaque; |
73 |
switch (addr & 0x3f) { |
74 |
case 0x00: |
75 |
return s->mr[s->current_mr];
|
76 |
case 0x04: |
77 |
return s->sr;
|
78 |
case 0x0c: |
79 |
{ |
80 |
uint8_t val; |
81 |
int i;
|
82 |
|
83 |
if (s->fifo_len == 0) |
84 |
return 0; |
85 |
|
86 |
val = s->fifo[0];
|
87 |
s->fifo_len--; |
88 |
for (i = 0; i < s->fifo_len; i++) |
89 |
s->fifo[i] = s->fifo[i + 1];
|
90 |
s->sr &= ~MCF_UART_FFULL; |
91 |
if (s->fifo_len == 0) |
92 |
s->sr &= ~MCF_UART_RxRDY; |
93 |
mcf_uart_update(s); |
94 |
qemu_chr_accept_input(s->chr); |
95 |
return val;
|
96 |
} |
97 |
case 0x10: |
98 |
/* TODO: Implement IPCR. */
|
99 |
return 0; |
100 |
case 0x14: |
101 |
return s->isr;
|
102 |
case 0x18: |
103 |
return s->bg1;
|
104 |
case 0x1c: |
105 |
return s->bg2;
|
106 |
default:
|
107 |
return 0; |
108 |
} |
109 |
} |
110 |
|
111 |
/* Update TxRDY flag and set data if present and enabled. */
|
112 |
static void mcf_uart_do_tx(mcf_uart_state *s) |
113 |
{ |
114 |
if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { |
115 |
if (s->chr)
|
116 |
qemu_chr_fe_write(s->chr, (unsigned char *)&s->tb, 1); |
117 |
s->sr |= MCF_UART_TxEMP; |
118 |
} |
119 |
if (s->tx_enabled) {
|
120 |
s->sr |= MCF_UART_TxRDY; |
121 |
} else {
|
122 |
s->sr &= ~MCF_UART_TxRDY; |
123 |
} |
124 |
} |
125 |
|
126 |
static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) |
127 |
{ |
128 |
/* Misc command. */
|
129 |
switch ((cmd >> 4) & 3) { |
130 |
case 0: /* No-op. */ |
131 |
break;
|
132 |
case 1: /* Reset mode register pointer. */ |
133 |
s->current_mr = 0;
|
134 |
break;
|
135 |
case 2: /* Reset receiver. */ |
136 |
s->rx_enabled = 0;
|
137 |
s->fifo_len = 0;
|
138 |
s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL); |
139 |
break;
|
140 |
case 3: /* Reset transmitter. */ |
141 |
s->tx_enabled = 0;
|
142 |
s->sr |= MCF_UART_TxEMP; |
143 |
s->sr &= ~MCF_UART_TxRDY; |
144 |
break;
|
145 |
case 4: /* Reset error status. */ |
146 |
break;
|
147 |
case 5: /* Reset break-change interrupt. */ |
148 |
s->isr &= ~MCF_UART_DBINT; |
149 |
break;
|
150 |
case 6: /* Start break. */ |
151 |
case 7: /* Stop break. */ |
152 |
break;
|
153 |
} |
154 |
|
155 |
/* Transmitter command. */
|
156 |
switch ((cmd >> 2) & 3) { |
157 |
case 0: /* No-op. */ |
158 |
break;
|
159 |
case 1: /* Enable. */ |
160 |
s->tx_enabled = 1;
|
161 |
mcf_uart_do_tx(s); |
162 |
break;
|
163 |
case 2: /* Disable. */ |
164 |
s->tx_enabled = 0;
|
165 |
mcf_uart_do_tx(s); |
166 |
break;
|
167 |
case 3: /* Reserved. */ |
168 |
fprintf(stderr, "mcf_uart: Bad TX command\n");
|
169 |
break;
|
170 |
} |
171 |
|
172 |
/* Receiver command. */
|
173 |
switch (cmd & 3) { |
174 |
case 0: /* No-op. */ |
175 |
break;
|
176 |
case 1: /* Enable. */ |
177 |
s->rx_enabled = 1;
|
178 |
break;
|
179 |
case 2: |
180 |
s->rx_enabled = 0;
|
181 |
break;
|
182 |
case 3: /* Reserved. */ |
183 |
fprintf(stderr, "mcf_uart: Bad RX command\n");
|
184 |
break;
|
185 |
} |
186 |
} |
187 |
|
188 |
void mcf_uart_write(void *opaque, hwaddr addr, |
189 |
uint64_t val, unsigned size)
|
190 |
{ |
191 |
mcf_uart_state *s = (mcf_uart_state *)opaque; |
192 |
switch (addr & 0x3f) { |
193 |
case 0x00: |
194 |
s->mr[s->current_mr] = val; |
195 |
s->current_mr = 1;
|
196 |
break;
|
197 |
case 0x04: |
198 |
/* CSR is ignored. */
|
199 |
break;
|
200 |
case 0x08: /* Command Register. */ |
201 |
mcf_do_command(s, val); |
202 |
break;
|
203 |
case 0x0c: /* Transmit Buffer. */ |
204 |
s->sr &= ~MCF_UART_TxEMP; |
205 |
s->tb = val; |
206 |
mcf_uart_do_tx(s); |
207 |
break;
|
208 |
case 0x10: |
209 |
/* ACR is ignored. */
|
210 |
break;
|
211 |
case 0x14: |
212 |
s->imr = val; |
213 |
break;
|
214 |
default:
|
215 |
break;
|
216 |
} |
217 |
mcf_uart_update(s); |
218 |
} |
219 |
|
220 |
static void mcf_uart_reset(mcf_uart_state *s) |
221 |
{ |
222 |
s->fifo_len = 0;
|
223 |
s->mr[0] = 0; |
224 |
s->mr[1] = 0; |
225 |
s->sr = MCF_UART_TxEMP; |
226 |
s->tx_enabled = 0;
|
227 |
s->rx_enabled = 0;
|
228 |
s->isr = 0;
|
229 |
s->imr = 0;
|
230 |
} |
231 |
|
232 |
static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) |
233 |
{ |
234 |
/* Break events overwrite the last byte if the fifo is full. */
|
235 |
if (s->fifo_len == 4) |
236 |
s->fifo_len--; |
237 |
|
238 |
s->fifo[s->fifo_len] = data; |
239 |
s->fifo_len++; |
240 |
s->sr |= MCF_UART_RxRDY; |
241 |
if (s->fifo_len == 4) |
242 |
s->sr |= MCF_UART_FFULL; |
243 |
|
244 |
mcf_uart_update(s); |
245 |
} |
246 |
|
247 |
static void mcf_uart_event(void *opaque, int event) |
248 |
{ |
249 |
mcf_uart_state *s = (mcf_uart_state *)opaque; |
250 |
|
251 |
switch (event) {
|
252 |
case CHR_EVENT_BREAK:
|
253 |
s->isr |= MCF_UART_DBINT; |
254 |
mcf_uart_push_byte(s, 0);
|
255 |
break;
|
256 |
default:
|
257 |
break;
|
258 |
} |
259 |
} |
260 |
|
261 |
static int mcf_uart_can_receive(void *opaque) |
262 |
{ |
263 |
mcf_uart_state *s = (mcf_uart_state *)opaque; |
264 |
|
265 |
return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; |
266 |
} |
267 |
|
268 |
static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) |
269 |
{ |
270 |
mcf_uart_state *s = (mcf_uart_state *)opaque; |
271 |
|
272 |
mcf_uart_push_byte(s, buf[0]);
|
273 |
} |
274 |
|
275 |
void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
|
276 |
{ |
277 |
mcf_uart_state *s; |
278 |
|
279 |
s = g_malloc0(sizeof(mcf_uart_state));
|
280 |
s->chr = chr; |
281 |
s->irq = irq; |
282 |
if (chr) {
|
283 |
qemu_chr_fe_claim_no_fail(chr); |
284 |
qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, |
285 |
mcf_uart_event, s); |
286 |
} |
287 |
mcf_uart_reset(s); |
288 |
return s;
|
289 |
} |
290 |
|
291 |
static const MemoryRegionOps mcf_uart_ops = { |
292 |
.read = mcf_uart_read, |
293 |
.write = mcf_uart_write, |
294 |
.endianness = DEVICE_NATIVE_ENDIAN, |
295 |
}; |
296 |
|
297 |
void mcf_uart_mm_init(MemoryRegion *sysmem,
|
298 |
hwaddr base, |
299 |
qemu_irq irq, |
300 |
CharDriverState *chr) |
301 |
{ |
302 |
mcf_uart_state *s; |
303 |
|
304 |
s = mcf_uart_init(irq, chr); |
305 |
memory_region_init_io(&s->iomem, &mcf_uart_ops, s, "uart", 0x40); |
306 |
memory_region_add_subregion(sysmem, base, &s->iomem); |
307 |
} |