Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ 43997225

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 "exec-memory.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
    MemoryRegion iomem;
44
    MemoryRegion iomem_p4;
45
    MemoryRegion iomem_a7;
46
    uint8_t smr;
47
    uint8_t brr;
48
    uint8_t scr;
49
    uint8_t dr; /* ftdr / tdr */
50
    uint8_t sr; /* fsr / ssr */
51
    uint16_t fcr;
52
    uint8_t sptr;
53

    
54
    uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
55
    uint8_t rx_cnt;
56
    uint8_t rx_tail;
57
    uint8_t rx_head;
58

    
59
    int freq;
60
    int feat;
61
    int flags;
62
    int rtrg;
63

    
64
    CharDriverState *chr;
65

    
66
    qemu_irq eri;
67
    qemu_irq rxi;
68
    qemu_irq txi;
69
    qemu_irq tei;
70
    qemu_irq bri;
71
} sh_serial_state;
72

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

    
81
static void sh_serial_write(void *opaque, target_phys_addr_t offs,
82
                            uint64_t val, unsigned size)
83
{
84
    sh_serial_state *s = opaque;
85
    unsigned char ch;
86

    
87
#ifdef DEBUG_SERIAL
88
    printf("sh_serial: write offs=0x%02x val=0x%02x\n",
89
           offs, val);
90
#endif
91
    switch(offs) {
92
    case 0x00: /* SMR */
93
        s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
94
        return;
95
    case 0x04: /* BRR */
96
        s->brr = val;
97
        return;
98
    case 0x08: /* SCR */
99
        /* TODO : For SH7751, SCIF mask should be 0xfb. */
100
        s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
101
        if (!(val & (1 << 5)))
102
            s->flags |= SH_SERIAL_FLAG_TEND;
103
        if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
104
            qemu_set_irq(s->txi, val & (1 << 7));
105
        }
106
        if (!(val & (1 << 6))) {
107
            qemu_set_irq(s->rxi, 0);
108
        }
109
        return;
110
    case 0x0c: /* FTDR / TDR */
111
        if (s->chr) {
112
            ch = val;
113
            qemu_chr_fe_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) {
140
                    qemu_set_irq(s->rxi, 0);
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
        switch(offs) {
175
#if 0
176
        case 0x0c:
177
            ret = s->dr;
178
            break;
179
        case 0x10:
180
            ret = 0;
181
            break;
182
#endif
183
        case 0x1c:
184
            s->sptr = val & 0x8f;
185
            return;
186
        }
187
    }
188

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

    
193
static uint64_t sh_serial_read(void *opaque, target_phys_addr_t offs,
194
                               unsigned size)
195
{
196
    sh_serial_state *s = opaque;
197
    uint32_t ret = ~0;
198

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

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

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

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

    
294
    return ret;
295
}
296

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

    
302
static void sh_serial_receive_break(sh_serial_state *s)
303
{
304
    if (s->feat & SH_SERIAL_FEAT_SCIF)
305
        s->sr |= (1 << 4);
306
}
307

    
308
static int sh_serial_can_receive1(void *opaque)
309
{
310
    sh_serial_state *s = opaque;
311
    return sh_serial_can_receive(s);
312
}
313

    
314
static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
315
{
316
    sh_serial_state *s = opaque;
317

    
318
    if (s->feat & SH_SERIAL_FEAT_SCIF) {
319
        int i;
320
        for (i = 0; i < size; i++) {
321
            if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
322
                s->rx_fifo[s->rx_head++] = buf[i];
323
                if (s->rx_head == SH_RX_FIFO_LENGTH) {
324
                    s->rx_head = 0;
325
                }
326
                s->rx_cnt++;
327
                if (s->rx_cnt >= s->rtrg) {
328
                    s->flags |= SH_SERIAL_FLAG_RDF;
329
                    if (s->scr & (1 << 6) && s->rxi) {
330
                        qemu_set_irq(s->rxi, 1);
331
                    }
332
                }
333
            }
334
        }
335
    } else {
336
        s->rx_fifo[0] = buf[0];
337
    }
338
}
339

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

    
347
static const MemoryRegionOps sh_serial_ops = {
348
    .read = sh_serial_read,
349
    .write = sh_serial_write,
350
    .endianness = DEVICE_NATIVE_ENDIAN,
351
};
352

    
353
void sh_serial_init(MemoryRegion *sysmem,
354
                    target_phys_addr_t base, int feat,
355
                    uint32_t freq, CharDriverState *chr,
356
                    qemu_irq eri_source,
357
                    qemu_irq rxi_source,
358
                    qemu_irq txi_source,
359
                    qemu_irq tei_source,
360
                    qemu_irq bri_source)
361
{
362
    sh_serial_state *s;
363

    
364
    s = g_malloc0(sizeof(sh_serial_state));
365

    
366
    s->feat = feat;
367
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
368
    s->rtrg = 1;
369

    
370
    s->smr = 0;
371
    s->brr = 0xff;
372
    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
373
    s->sptr = 0;
374

    
375
    if (feat & SH_SERIAL_FEAT_SCIF) {
376
        s->fcr = 0;
377
    }
378
    else {
379
        s->dr = 0xff;
380
    }
381

    
382
    sh_serial_clear_fifo(s);
383

    
384
    memory_region_init_io(&s->iomem, &sh_serial_ops, s,
385
                          "serial", 0x100000000ULL);
386

    
387
    memory_region_init_alias(&s->iomem_p4, "serial-p4", &s->iomem,
388
                             0, 0x28);
389
    memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
390

    
391
    memory_region_init_alias(&s->iomem_a7, "serial-a7", &s->iomem,
392
                             0, 0x28);
393
    memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
394

    
395
    s->chr = chr;
396

    
397
    if (chr)
398
        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
399
                              sh_serial_event, s);
400

    
401
    s->eri = eri_source;
402
    s->rxi = rxi_source;
403
    s->txi = txi_source;
404
    s->tei = tei_source;
405
    s->bri = bri_source;
406
}