Statistics
| Branch: | Revision:

root / hw / sh_serial.c @ a8170e5e

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, hwaddr 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%02"
190
            HWADDR_PRIx "\n", offs);
191
    abort();
192
}
193

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

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

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

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

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

    
296
    return ret;
297
}
298

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

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

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

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

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

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

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

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

    
366
    s = g_malloc0(sizeof(sh_serial_state));
367

    
368
    s->feat = feat;
369
    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
370
    s->rtrg = 1;
371

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

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

    
384
    sh_serial_clear_fifo(s);
385

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

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

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

    
397
    s->chr = chr;
398

    
399
    if (chr)
400
        qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
401
                              sh_serial_event, s);
402

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