Statistics
| Branch: | Revision:

root / hw / mcf_uart.c @ 927d4878

History | View | Annotate | Download (7 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.h"
9
#include "mcf.h"
10
#include "char/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_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive,
284
                              mcf_uart_event, s);
285
    }
286
    mcf_uart_reset(s);
287
    return s;
288
}
289

    
290
static const MemoryRegionOps mcf_uart_ops = {
291
    .read = mcf_uart_read,
292
    .write = mcf_uart_write,
293
    .endianness = DEVICE_NATIVE_ENDIAN,
294
};
295

    
296
void mcf_uart_mm_init(MemoryRegion *sysmem,
297
                      hwaddr base,
298
                      qemu_irq irq,
299
                      CharDriverState *chr)
300
{
301
    mcf_uart_state *s;
302

    
303
    s = mcf_uart_init(irq, chr);
304
    memory_region_init_io(&s->iomem, &mcf_uart_ops, s, "uart", 0x40);
305
    memory_region_add_subregion(sysmem, base, &s->iomem);
306
}