Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ 1a1ea6f0

History | View | Annotate | Download (10.2 kB)

1
/*
2
 * QEMU SCI/SCIF serial port emulation
3
 *
4
 * Copyright (c) 2007 Magnus Damm
5
 *
6
 * Based on serial.c - QEMU 16450 UART emulation
7
 * Copyright (c) 2003-2004 Fabrice Bellard
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a copy
10
 * of this software and associated documentation files (the "Software"), to deal
11
 * in the Software without restriction, including without limitation the rights
12
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
 * copies of the Software, and to permit persons to whom the Software is
14
 * furnished to do so, subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included in
17
 * all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
 * THE SOFTWARE.
26
 */
27
#include "hw.h"
28
#include "sh.h"
29
#include "qemu-char.h"
30

    
31
//#define DEBUG_SERIAL
32

    
33
#define SH_SERIAL_FLAG_TEND (1 << 0)
34
#define SH_SERIAL_FLAG_TDE  (1 << 1)
35
#define SH_SERIAL_FLAG_RDF  (1 << 2)
36
#define SH_SERIAL_FLAG_BRK  (1 << 3)
37
#define SH_SERIAL_FLAG_DR   (1 << 4)
38

    
39
#define SH_RX_FIFO_LENGTH (16)
40

    
41
typedef struct {
42
    uint8_t smr;
43
    uint8_t brr;
44
    uint8_t scr;
45
    uint8_t dr; /* ftdr / tdr */
46
    uint8_t sr; /* fsr / ssr */
47
    uint16_t fcr;
48
    uint8_t sptr;
49

    
50
    uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
51
    uint8_t rx_cnt;
52
    uint8_t rx_tail;
53
    uint8_t rx_head;
54

    
55
    int freq;
56
    int feat;
57
    int flags;
58
    int rtrg;
59

    
60
    CharDriverState *chr;
61

    
62
    qemu_irq eri;
63
    qemu_irq rxi;
64
    qemu_irq txi;
65
    qemu_irq tei;
66
    qemu_irq bri;
67
} sh_serial_state;
68

    
69
static void sh_serial_clear_fifo(sh_serial_state * s)
70
{
71
    memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
72
    s->rx_cnt = 0;
73
    s->rx_head = 0;
74
    s->rx_tail = 0;
75
}
76

    
77
static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val)
78
{
79
    sh_serial_state *s = opaque;
80
    unsigned char ch;
81

    
82
#ifdef DEBUG_SERIAL
83
    printf("sh_serial: write offs=0x%02x val=0x%02x\n",
84
           offs, val);
85
#endif
86
    switch(offs) {
87
    case 0x00: /* SMR */
88
        s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
89
        return;
90
    case 0x04: /* BRR */
91
        s->brr = val;
92
        return;
93
    case 0x08: /* SCR */
94
        /* TODO : For SH7751, SCIF mask should be 0xfb. */
95
        s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
96
        if (!(val & (1 << 5)))
97
            s->flags |= SH_SERIAL_FLAG_TEND;
98
        if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
99
            qemu_set_irq(s->txi, val & (1 << 7));
100
        }
101
        if (!(val & (1 << 6))) {
102
            qemu_set_irq(s->rxi, 0);
103
        }
104
        return;
105
    case 0x0c: /* FTDR / TDR */
106
        if (s->chr) {
107
            ch = val;
108
            qemu_chr_write(s->chr, &ch, 1);
109
        }
110
        s->dr = val;
111
        s->flags &= ~SH_SERIAL_FLAG_TDE;
112
        return;
113
#if 0
114
    case 0x14: /* FRDR / RDR */
115
        ret = 0;
116
        break;
117
#endif
118
    }
119
    if (s->feat & SH_SERIAL_FEAT_SCIF) {
120
        switch(offs) {
121
        case 0x10: /* FSR */
122
            if (!(val & (1 << 6)))
123
                s->flags &= ~SH_SERIAL_FLAG_TEND;
124
            if (!(val & (1 << 5)))
125
                s->flags &= ~SH_SERIAL_FLAG_TDE;
126
            if (!(val & (1 << 4)))
127
                s->flags &= ~SH_SERIAL_FLAG_BRK;
128
            if (!(val & (1 << 1)))
129
                s->flags &= ~SH_SERIAL_FLAG_RDF;
130
            if (!(val & (1 << 0)))
131
                s->flags &= ~SH_SERIAL_FLAG_DR;
132

    
133
            if (!(val & (1 << 1)) || !(val & (1 << 0))) {
134
                if (s->rxi) {
135
                    qemu_set_irq(s->rxi, 0);
136
                }
137
            }
138
            return;
139
        case 0x18: /* FCR */
140
            s->fcr = val;
141
            switch ((val >> 6) & 3) {
142
            case 0:
143
                s->rtrg = 1;
144
                break;
145
            case 1:
146
                s->rtrg = 4;
147
                break;
148
            case 2:
149
                s->rtrg = 8;
150
                break;
151
            case 3:
152
                s->rtrg = 14;
153
                break;
154
            }
155
            if (val & (1 << 1)) {
156
                sh_serial_clear_fifo(s);
157
                s->sr &= ~(1 << 1);
158
            }
159

    
160
            return;
161
        case 0x20: /* SPTR */
162
            s->sptr = val & 0xf3;
163
            return;
164
        case 0x24: /* LSR */
165
            return;
166
        }
167
    }
168
    else {
169
        switch(offs) {
170
#if 0
171
        case 0x0c:
172
            ret = s->dr;
173
            break;
174
        case 0x10:
175
            ret = 0;
176
            break;
177
#endif
178
        case 0x1c:
179
            s->sptr = val & 0x8f;
180
            return;
181
        }
182
    }
183

    
184
    fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
185
    abort();
186
}
187

    
188
static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs)
189
{
190
    sh_serial_state *s = opaque;
191
    uint32_t ret = ~0;
192

    
193
#if 0
194
    switch(offs) {
195
    case 0x00:
196
        ret = s->smr;
197
        break;
198
    case 0x04:
199
        ret = s->brr;
200
        break;
201
    case 0x08:
202
        ret = s->scr;
203
        break;
204
    case 0x14:
205
        ret = 0;
206
        break;
207
    }
208
#endif
209
    if (s->feat & SH_SERIAL_FEAT_SCIF) {
210
        switch(offs) {
211
        case 0x00: /* SMR */
212
            ret = s->smr;
213
            break;
214
        case 0x08: /* SCR */
215
            ret = s->scr;
216
            break;
217
        case 0x10: /* FSR */
218
            ret = 0;
219
            if (s->flags & SH_SERIAL_FLAG_TEND)
220
                ret |= (1 << 6);
221
            if (s->flags & SH_SERIAL_FLAG_TDE)
222
                ret |= (1 << 5);
223
            if (s->flags & SH_SERIAL_FLAG_BRK)
224
                ret |= (1 << 4);
225
            if (s->flags & SH_SERIAL_FLAG_RDF)
226
                ret |= (1 << 1);
227
            if (s->flags & SH_SERIAL_FLAG_DR)
228
                ret |= (1 << 0);
229

    
230
            if (s->scr & (1 << 5))
231
                s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
232

    
233
            break;
234
        case 0x14:
235
            if (s->rx_cnt > 0) {
236
                ret = s->rx_fifo[s->rx_tail++];
237
                s->rx_cnt--;
238
                if (s->rx_tail == SH_RX_FIFO_LENGTH)
239
                    s->rx_tail = 0;
240
                if (s->rx_cnt < s->rtrg)
241
                    s->flags &= ~SH_SERIAL_FLAG_RDF;
242
            }
243
            break;
244
#if 0
245
        case 0x18:
246
            ret = s->fcr;
247
            break;
248
#endif
249
        case 0x1c:
250
            ret = s->rx_cnt;
251
            break;
252
        case 0x20:
253
            ret = s->sptr;
254
            break;
255
        case 0x24:
256
            ret = 0;
257
            break;
258
        }
259
    }
260
    else {
261
        switch(offs) {
262
#if 0
263
        case 0x0c:
264
            ret = s->dr;
265
            break;
266
        case 0x10:
267
            ret = 0;
268
            break;
269
        case 0x14:
270
            ret = s->rx_fifo[0];
271
            break;
272
#endif
273
        case 0x1c:
274
            ret = s->sptr;
275
            break;
276
        }
277
    }
278
#ifdef DEBUG_SERIAL
279
    printf("sh_serial: read offs=0x%02x val=0x%x\n",
280
           offs, ret);
281
#endif
282

    
283
    if (ret & ~((1 << 16) - 1)) {
284
        fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs);
285
        abort();
286
    }
287

    
288
    return ret;
289
}
290

    
291
static int sh_serial_can_receive(sh_serial_state *s)
292
{
293
    return s->scr & (1 << 4);
294
}
295

    
296
static void sh_serial_receive_byte(sh_serial_state *s, int ch)
297
{
298
    if (s->feat & SH_SERIAL_FEAT_SCIF) {
299
        if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
300
            s->rx_fifo[s->rx_head++] = ch;
301
            if (s->rx_head == SH_RX_FIFO_LENGTH)
302
                s->rx_head = 0;
303
            s->rx_cnt++;
304
            if (s->rx_cnt >= s->rtrg) {
305
                s->flags |= SH_SERIAL_FLAG_RDF;
306
                if (s->scr & (1 << 6) && s->rxi) {
307
                    qemu_set_irq(s->rxi, 1);
308
                }
309
            }
310
        }
311
    } else {
312
        s->rx_fifo[0] = ch;
313
    }
314
}
315

    
316
static void sh_serial_receive_break(sh_serial_state *s)
317
{
318
    if (s->feat & SH_SERIAL_FEAT_SCIF)
319
        s->sr |= (1 << 4);
320
}
321

    
322
static int sh_serial_can_receive1(void *opaque)
323
{
324
    sh_serial_state *s = opaque;
325
    return sh_serial_can_receive(s);
326
}
327

    
328
static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
329
{
330
    sh_serial_state *s = opaque;
331
    sh_serial_receive_byte(s, buf[0]);
332
}
333

    
334
static void sh_serial_event(void *opaque, int event)
335
{
336
    sh_serial_state *s = opaque;
337
    if (event == CHR_EVENT_BREAK)
338
        sh_serial_receive_break(s);
339
}
340

    
341
static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr)
342
{
343
    sh_serial_state *s = opaque;
344
    return sh_serial_ioport_read(s, addr);
345
}
346

    
347
static void sh_serial_write (void *opaque,
348
                             target_phys_addr_t addr, uint32_t value)
349
{
350
    sh_serial_state *s = opaque;
351
    sh_serial_ioport_write(s, addr, value);
352
}
353

    
354
static CPUReadMemoryFunc * const sh_serial_readfn[] = {
355
    &sh_serial_read,
356
    &sh_serial_read,
357
    &sh_serial_read,
358
};
359

    
360
static CPUWriteMemoryFunc * const sh_serial_writefn[] = {
361
    &sh_serial_write,
362
    &sh_serial_write,
363
    &sh_serial_write,
364
};
365

    
366
void sh_serial_init (target_phys_addr_t base, int feat,
367
                     uint32_t freq, CharDriverState *chr,
368
                     qemu_irq eri_source,
369
                     qemu_irq rxi_source,
370
                     qemu_irq txi_source,
371
                     qemu_irq tei_source,
372
                     qemu_irq bri_source)
373
{
374
    sh_serial_state *s;
375
    int s_io_memory;
376

    
377
    s = qemu_mallocz(sizeof(sh_serial_state));
378

    
379
    s->feat = feat;
380
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
381
    s->rtrg = 1;
382

    
383
    s->smr = 0;
384
    s->brr = 0xff;
385
    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
386
    s->sptr = 0;
387

    
388
    if (feat & SH_SERIAL_FEAT_SCIF) {
389
        s->fcr = 0;
390
    }
391
    else {
392
        s->dr = 0xff;
393
    }
394

    
395
    sh_serial_clear_fifo(s);
396

    
397
    s_io_memory = cpu_register_io_memory(sh_serial_readfn,
398
                                         sh_serial_writefn, s);
399
    cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory);
400
    cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory);
401

    
402
    s->chr = chr;
403

    
404
    if (chr)
405
        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
406
                              sh_serial_event, s);
407

    
408
    s->eri = eri_source;
409
    s->rxi = rxi_source;
410
    s->txi = txi_source;
411
    s->tei = tei_source;
412
    s->bri = bri_source;
413
}