Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ 5c16736a

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
#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
    int freq;
57
    int feat;
58
    int flags;
59
    int rtrg;
60

    
61
    CharDriverState *chr;
62

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

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

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

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

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

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

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

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

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

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

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

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

    
289
    return ret;
290
}
291

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

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

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

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

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

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

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

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

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

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

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

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

    
382
    s->feat = feat;
383
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
384
    s->rtrg = 1;
385

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

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

    
398
    sh_serial_clear_fifo(s);
399

    
400
    s_io_memory = cpu_register_io_memory(0, sh_serial_readfn,
401
                                         sh_serial_writefn, s);
402
    cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory);
403
    cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory);
404

    
405
    s->chr = chr;
406

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

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