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