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