Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ 4e7ed2d1

History | View | Annotate | Download (10.3 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
#include <assert.h>
31

    
32
//#define DEBUG_SERIAL
33

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

    
40
#define SH_RX_FIFO_LENGTH (16)
41

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

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

    
56
    target_phys_addr_t base;
57
    int freq;
58
    int feat;
59
    int flags;
60
    int rtrg;
61

    
62
    CharDriverState *chr;
63

    
64
    qemu_irq eri;
65
    qemu_irq rxi;
66
    qemu_irq txi;
67
    qemu_irq tei;
68
    qemu_irq bri;
69
} sh_serial_state;
70

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

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

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

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

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

    
186
    fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
187
    assert(0);
188
}
189

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

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

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

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

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

    
290
    return ret;
291
}
292

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

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

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

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

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

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

    
343
static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr)
344
{
345
    sh_serial_state *s = opaque;
346
    return sh_serial_ioport_read(s, addr - s->base);
347
}
348

    
349
static void sh_serial_write (void *opaque,
350
                             target_phys_addr_t addr, uint32_t value)
351
{
352
    sh_serial_state *s = opaque;
353
    sh_serial_ioport_write(s, addr - s->base, value);
354
}
355

    
356
static CPUReadMemoryFunc *sh_serial_readfn[] = {
357
    &sh_serial_read,
358
    &sh_serial_read,
359
    &sh_serial_read,
360
};
361

    
362
static CPUWriteMemoryFunc *sh_serial_writefn[] = {
363
    &sh_serial_write,
364
    &sh_serial_write,
365
    &sh_serial_write,
366
};
367

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

    
379
    s = qemu_mallocz(sizeof(sh_serial_state));
380
    if (!s)
381
        return;
382

    
383
    s->base = base;
384
    s->feat = feat;
385
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
386
    s->rtrg = 1;
387

    
388
    s->smr = 0;
389
    s->brr = 0xff;
390
    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
391
    s->sptr = 0;
392

    
393
    if (feat & SH_SERIAL_FEAT_SCIF) {
394
        s->fcr = 0;
395
    }
396
    else {
397
        s->dr = 0xff;
398
    }
399

    
400
    sh_serial_clear_fifo(s);
401

    
402
    s_io_memory = cpu_register_io_memory(0, sh_serial_readfn,
403
                                         sh_serial_writefn, s);
404
    cpu_register_physical_memory(base, 0x28, s_io_memory);
405

    
406
    s->chr = chr;
407

    
408
    if (chr)
409
        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
410
                              sh_serial_event, s);
411

    
412
    s->eri = eri_source;
413
    s->rxi = rxi_source;
414
    s->txi = txi_source;
415
    s->tei = tei_source;
416
    s->bri = bri_source;
417
}