Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ 1ae26a18

History | View | Annotate | Download (10.6 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
    struct intc_source *eri;
65
    struct intc_source *rxi;
66
    struct intc_source *txi;
67
    struct intc_source *tei;
68
    struct intc_source *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
            if ((val & (1 << 7)) && !(s->txi->asserted))
102
                sh_intc_toggle_source(s->txi, 0, 1);
103
            else if (!(val & (1 << 7)) && s->txi->asserted)
104
                sh_intc_toggle_source(s->txi, 0, -1);
105
        }
106
        if (!(val & (1 << 6)) && s->rxi->asserted) {
107
            sh_intc_toggle_source(s->rxi, 0, -1);
108
        }
109
        return;
110
    case 0x0c: /* FTDR / TDR */
111
        if (s->chr) {
112
            ch = val;
113
            qemu_chr_write(s->chr, &ch, 1);
114
        }
115
        s->dr = val;
116
        s->flags &= ~SH_SERIAL_FLAG_TDE;
117
        return;
118
#if 0
119
    case 0x14: /* FRDR / RDR */
120
        ret = 0;
121
        break;
122
#endif
123
    }
124
    if (s->feat & SH_SERIAL_FEAT_SCIF) {
125
        switch(offs) {
126
        case 0x10: /* FSR */
127
            if (!(val & (1 << 6)))
128
                s->flags &= ~SH_SERIAL_FLAG_TEND;
129
            if (!(val & (1 << 5)))
130
                s->flags &= ~SH_SERIAL_FLAG_TDE;
131
            if (!(val & (1 << 4)))
132
                s->flags &= ~SH_SERIAL_FLAG_BRK;
133
            if (!(val & (1 << 1)))
134
                s->flags &= ~SH_SERIAL_FLAG_RDF;
135
            if (!(val & (1 << 0)))
136
                s->flags &= ~SH_SERIAL_FLAG_DR;
137

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

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

    
189
    fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
190
    assert(0);
191
}
192

    
193
static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs)
194
{
195
    sh_serial_state *s = opaque;
196
    uint32_t ret = ~0;
197

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

    
235
            if (s->scr & (1 << 5))
236
                s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
237

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

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

    
293
    return ret;
294
}
295

    
296
static int sh_serial_can_receive(sh_serial_state *s)
297
{
298
    return s->scr & (1 << 4);
299
}
300

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

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

    
327
static int sh_serial_can_receive1(void *opaque)
328
{
329
    sh_serial_state *s = opaque;
330
    return sh_serial_can_receive(s);
331
}
332

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

    
339
static void sh_serial_event(void *opaque, int event)
340
{
341
    sh_serial_state *s = opaque;
342
    if (event == CHR_EVENT_BREAK)
343
        sh_serial_receive_break(s);
344
}
345

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

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

    
359
static CPUReadMemoryFunc *sh_serial_readfn[] = {
360
    &sh_serial_read,
361
    &sh_serial_read,
362
    &sh_serial_read,
363
};
364

    
365
static CPUWriteMemoryFunc *sh_serial_writefn[] = {
366
    &sh_serial_write,
367
    &sh_serial_write,
368
    &sh_serial_write,
369
};
370

    
371
void sh_serial_init (target_phys_addr_t base, int feat,
372
                     uint32_t freq, CharDriverState *chr,
373
                     struct intc_source *eri_source,
374
                     struct intc_source *rxi_source,
375
                     struct intc_source *txi_source,
376
                     struct intc_source *tei_source,
377
                     struct intc_source *bri_source)
378
{
379
    sh_serial_state *s;
380
    int s_io_memory;
381

    
382
    s = qemu_mallocz(sizeof(sh_serial_state));
383
    if (!s)
384
        return;
385

    
386
    s->base = base;
387
    s->feat = feat;
388
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
389
    s->rtrg = 1;
390

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

    
396
    if (feat & SH_SERIAL_FEAT_SCIF) {
397
        s->fcr = 0;
398
    }
399
    else {
400
        s->dr = 0xff;
401
    }
402

    
403
    sh_serial_clear_fifo(s);
404

    
405
    s_io_memory = cpu_register_io_memory(0, sh_serial_readfn,
406
                                         sh_serial_writefn, s);
407
    cpu_register_physical_memory(base, 0x28, s_io_memory);
408

    
409
    s->chr = chr;
410

    
411
    if (chr)
412
        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
413
                              sh_serial_event, s);
414

    
415
    s->eri = eri_source;
416
    s->rxi = rxi_source;
417
    s->txi = txi_source;
418
    s->tei = tei_source;
419
    s->bri = bri_source;
420
}