qemu

Форк
0
/
exynos4210_uart.c 
738 строк · 21.7 Кб
1
/*
2
 *  Exynos4210 UART Emulation
3
 *
4
 *  Copyright (C) 2011 Samsung Electronics Co Ltd.
5
 *    Maksim Kozlov, <m.kozlov@samsung.com>
6
 *
7
 *  This program is free software; you can redistribute it and/or modify it
8
 *  under the terms of the GNU General Public License as published by the
9
 *  Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful, but WITHOUT
13
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14
 *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15
 *  for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License along
18
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21

22
#include "qemu/osdep.h"
23
#include "hw/sysbus.h"
24
#include "migration/vmstate.h"
25
#include "qapi/error.h"
26
#include "qemu/error-report.h"
27
#include "qemu/module.h"
28
#include "qemu/timer.h"
29
#include "chardev/char-fe.h"
30
#include "chardev/char-serial.h"
31

32
#include "hw/arm/exynos4210.h"
33
#include "hw/irq.h"
34
#include "hw/qdev-properties.h"
35
#include "hw/qdev-properties-system.h"
36

37
#include "trace.h"
38
#include "qom/object.h"
39

40
/*
41
 *  Offsets for UART registers relative to SFR base address
42
 *  for UARTn
43
 *
44
 */
45
#define ULCON      0x0000 /* Line Control             */
46
#define UCON       0x0004 /* Control                  */
47
#define UFCON      0x0008 /* FIFO Control             */
48
#define UMCON      0x000C /* Modem Control            */
49
#define UTRSTAT    0x0010 /* Tx/Rx Status             */
50
#define UERSTAT    0x0014 /* UART Error Status        */
51
#define UFSTAT     0x0018 /* FIFO Status              */
52
#define UMSTAT     0x001C /* Modem Status             */
53
#define UTXH       0x0020 /* Transmit Buffer          */
54
#define URXH       0x0024 /* Receive Buffer           */
55
#define UBRDIV     0x0028 /* Baud Rate Divisor        */
56
#define UFRACVAL   0x002C /* Divisor Fractional Value */
57
#define UINTP      0x0030 /* Interrupt Pending        */
58
#define UINTSP     0x0034 /* Interrupt Source Pending */
59
#define UINTM      0x0038 /* Interrupt Mask           */
60

61
/*
62
 * for indexing register in the uint32_t array
63
 *
64
 * 'reg' - register offset (see offsets definitions above)
65
 *
66
 */
67
#define I_(reg) (reg / sizeof(uint32_t))
68

69
typedef struct Exynos4210UartReg {
70
    const char         *name; /* the only reason is the debug output */
71
    hwaddr  offset;
72
    uint32_t            reset_value;
73
} Exynos4210UartReg;
74

75
static const Exynos4210UartReg exynos4210_uart_regs[] = {
76
    {"ULCON",    ULCON,    0x00000000},
77
    {"UCON",     UCON,     0x00003000},
78
    {"UFCON",    UFCON,    0x00000000},
79
    {"UMCON",    UMCON,    0x00000000},
80
    {"UTRSTAT",  UTRSTAT,  0x00000006}, /* RO */
81
    {"UERSTAT",  UERSTAT,  0x00000000}, /* RO */
82
    {"UFSTAT",   UFSTAT,   0x00000000}, /* RO */
83
    {"UMSTAT",   UMSTAT,   0x00000000}, /* RO */
84
    {"UTXH",     UTXH,     0x5c5c5c5c}, /* WO, undefined reset value*/
85
    {"URXH",     URXH,     0x00000000}, /* RO */
86
    {"UBRDIV",   UBRDIV,   0x00000000},
87
    {"UFRACVAL", UFRACVAL, 0x00000000},
88
    {"UINTP",    UINTP,    0x00000000},
89
    {"UINTSP",   UINTSP,   0x00000000},
90
    {"UINTM",    UINTM,    0x00000000},
91
};
92

93
#define EXYNOS4210_UART_REGS_MEM_SIZE    0x3C
94

95
/* UART FIFO Control */
96
#define UFCON_FIFO_ENABLE                    0x1
97
#define UFCON_Rx_FIFO_RESET                  0x2
98
#define UFCON_Tx_FIFO_RESET                  0x4
99
#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT    8
100
#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT)
101
#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT    4
102
#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT)
103

104
/* Uart FIFO Status */
105
#define UFSTAT_Rx_FIFO_COUNT        0xff
106
#define UFSTAT_Rx_FIFO_FULL         0x100
107
#define UFSTAT_Rx_FIFO_ERROR        0x200
108
#define UFSTAT_Tx_FIFO_COUNT_SHIFT  16
109
#define UFSTAT_Tx_FIFO_COUNT        (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT)
110
#define UFSTAT_Tx_FIFO_FULL_SHIFT   24
111
#define UFSTAT_Tx_FIFO_FULL         (1 << UFSTAT_Tx_FIFO_FULL_SHIFT)
112

113
/* UART Interrupt Source Pending */
114
#define UINTSP_RXD      0x1 /* Receive interrupt  */
115
#define UINTSP_ERROR    0x2 /* Error interrupt    */
116
#define UINTSP_TXD      0x4 /* Transmit interrupt */
117
#define UINTSP_MODEM    0x8 /* Modem interrupt    */
118

119
/* UART Line Control */
120
#define ULCON_IR_MODE_SHIFT   6
121
#define ULCON_PARITY_SHIFT    3
122
#define ULCON_STOP_BIT_SHIFT  1
123

124
/* UART Tx/Rx Status */
125
#define UTRSTAT_Rx_TIMEOUT              0x8
126
#define UTRSTAT_TRANSMITTER_EMPTY       0x4
127
#define UTRSTAT_Tx_BUFFER_EMPTY         0x2
128
#define UTRSTAT_Rx_BUFFER_DATA_READY    0x1
129

130
/* UART Error Status */
131
#define UERSTAT_OVERRUN  0x1
132
#define UERSTAT_PARITY   0x2
133
#define UERSTAT_FRAME    0x4
134
#define UERSTAT_BREAK    0x8
135

136
typedef struct {
137
    uint8_t    *data;
138
    uint32_t    sp, rp; /* store and retrieve pointers */
139
    uint32_t    size;
140
} Exynos4210UartFIFO;
141

142
#define TYPE_EXYNOS4210_UART "exynos4210.uart"
143
OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210UartState, EXYNOS4210_UART)
144

145
struct Exynos4210UartState {
146
    SysBusDevice parent_obj;
147

148
    MemoryRegion iomem;
149

150
    uint32_t             reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
151
    Exynos4210UartFIFO   rx;
152
    Exynos4210UartFIFO   tx;
153

154
    QEMUTimer *fifo_timeout_timer;
155
    uint64_t wordtime;        /* word time in ns */
156

157
    CharBackend       chr;
158
    qemu_irq          irq;
159
    qemu_irq          dmairq;
160

161
    uint32_t channel;
162

163
};
164

165

166
/* Used only for tracing */
167
static const char *exynos4210_uart_regname(hwaddr  offset)
168
{
169

170
    int i;
171

172
    for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) {
173
        if (offset == exynos4210_uart_regs[i].offset) {
174
            return exynos4210_uart_regs[i].name;
175
        }
176
    }
177

178
    return NULL;
179
}
180

181

182
static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch)
183
{
184
    q->data[q->sp] = ch;
185
    q->sp = (q->sp + 1) % q->size;
186
}
187

188
static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
189
{
190
    uint8_t ret = q->data[q->rp];
191
    q->rp = (q->rp + 1) % q->size;
192
    return  ret;
193
}
194

195
static int fifo_elements_number(const Exynos4210UartFIFO *q)
196
{
197
    if (q->sp < q->rp) {
198
        return q->size - q->rp + q->sp;
199
    }
200

201
    return q->sp - q->rp;
202
}
203

204
static int fifo_empty_elements_number(const Exynos4210UartFIFO *q)
205
{
206
    return q->size - fifo_elements_number(q);
207
}
208

209
static void fifo_reset(Exynos4210UartFIFO *q)
210
{
211
    g_free(q->data);
212
    q->data = NULL;
213

214
    q->data = g_malloc0(q->size);
215

216
    q->sp = 0;
217
    q->rp = 0;
218
}
219

220
static uint32_t exynos4210_uart_FIFO_trigger_level(uint32_t channel,
221
                                                   uint32_t reg)
222
{
223
    uint32_t level;
224

225
    switch (channel) {
226
    case 0:
227
        level = reg * 32;
228
        break;
229
    case 1:
230
    case 4:
231
        level = reg * 8;
232
        break;
233
    case 2:
234
    case 3:
235
        level = reg * 2;
236
        break;
237
    default:
238
        level = 0;
239
        trace_exynos_uart_channel_error(channel);
240
        break;
241
    }
242
    return level;
243
}
244

245
static uint32_t
246
exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s)
247
{
248
    uint32_t reg;
249

250
    reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >>
251
            UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT;
252

253
    return exynos4210_uart_FIFO_trigger_level(s->channel, reg);
254
}
255

256
static uint32_t
257
exynos4210_uart_Rx_FIFO_trigger_level(const Exynos4210UartState *s)
258
{
259
    uint32_t reg;
260

261
    reg = ((s->reg[I_(UFCON)] & UFCON_Rx_FIFO_TRIGGER_LEVEL) >>
262
            UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) + 1;
263

264
    return exynos4210_uart_FIFO_trigger_level(s->channel, reg);
265
}
266

267
/*
268
 * Update Rx DMA busy signal if Rx DMA is enabled. For simplicity,
269
 * mark DMA as busy if DMA is enabled and the receive buffer is empty.
270
 */
271
static void exynos4210_uart_update_dmabusy(Exynos4210UartState *s)
272
{
273
    bool rx_dma_enabled = (s->reg[I_(UCON)] & 0x03) == 0x02;
274
    uint32_t count = fifo_elements_number(&s->rx);
275

276
    if (rx_dma_enabled && !count) {
277
        qemu_irq_raise(s->dmairq);
278
        trace_exynos_uart_dmabusy(s->channel);
279
    } else {
280
        qemu_irq_lower(s->dmairq);
281
        trace_exynos_uart_dmaready(s->channel);
282
    }
283
}
284

285
static void exynos4210_uart_update_irq(Exynos4210UartState *s)
286
{
287
    /*
288
     * The Tx interrupt is always requested if the number of data in the
289
     * transmit FIFO is smaller than the trigger level.
290
     */
291
    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
292
        uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >>
293
                UFSTAT_Tx_FIFO_COUNT_SHIFT;
294

295
        if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) {
296
            s->reg[I_(UINTSP)] |= UINTSP_TXD;
297
        }
298

299
        /*
300
         * Rx interrupt if trigger level is reached or if rx timeout
301
         * interrupt is disabled and there is data in the receive buffer
302
         */
303
        count = fifo_elements_number(&s->rx);
304
        if ((count && !(s->reg[I_(UCON)] & 0x80)) ||
305
            count >= exynos4210_uart_Rx_FIFO_trigger_level(s)) {
306
            exynos4210_uart_update_dmabusy(s);
307
            s->reg[I_(UINTSP)] |= UINTSP_RXD;
308
            timer_del(s->fifo_timeout_timer);
309
        }
310
    } else if (s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) {
311
        exynos4210_uart_update_dmabusy(s);
312
        s->reg[I_(UINTSP)] |= UINTSP_RXD;
313
    }
314

315
    s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)];
316

317
    if (s->reg[I_(UINTP)]) {
318
        qemu_irq_raise(s->irq);
319
        trace_exynos_uart_irq_raised(s->channel, s->reg[I_(UINTP)]);
320
    } else {
321
        qemu_irq_lower(s->irq);
322
        trace_exynos_uart_irq_lowered(s->channel);
323
    }
324
}
325

326
static void exynos4210_uart_timeout_int(void *opaque)
327
{
328
    Exynos4210UartState *s = opaque;
329

330
    trace_exynos_uart_rx_timeout(s->channel, s->reg[I_(UTRSTAT)],
331
                                 s->reg[I_(UINTSP)]);
332

333
    if ((s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) ||
334
        (s->reg[I_(UCON)] & (1 << 11))) {
335
        s->reg[I_(UINTSP)] |= UINTSP_RXD;
336
        s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_TIMEOUT;
337
        exynos4210_uart_update_dmabusy(s);
338
        exynos4210_uart_update_irq(s);
339
    }
340
}
341

342
static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
343
{
344
    int speed, parity, data_bits, stop_bits;
345
    QEMUSerialSetParams ssp;
346
    uint64_t uclk_rate;
347

348
    if (s->reg[I_(UBRDIV)] == 0) {
349
        return;
350
    }
351

352
    if (s->reg[I_(ULCON)] & 0x20) {
353
        if (s->reg[I_(ULCON)] & 0x28) {
354
            parity = 'E';
355
        } else {
356
            parity = 'O';
357
        }
358
    } else {
359
        parity = 'N';
360
    }
361

362
    if (s->reg[I_(ULCON)] & 0x4) {
363
        stop_bits = 2;
364
    } else {
365
        stop_bits = 1;
366
    }
367

368
    data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
369

370
    uclk_rate = 24000000;
371

372
    speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
373
            (s->reg[I_(UFRACVAL)] & 0x7) + 16);
374

375
    ssp.speed     = speed;
376
    ssp.parity    = parity;
377
    ssp.data_bits = data_bits;
378
    ssp.stop_bits = stop_bits;
379

380
    s->wordtime = NANOSECONDS_PER_SECOND * (data_bits + stop_bits + 1) / speed;
381

382
    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
383

384
    trace_exynos_uart_update_params(
385
                s->channel, speed, parity, data_bits, stop_bits, s->wordtime);
386
}
387

388
static void exynos4210_uart_rx_timeout_set(Exynos4210UartState *s)
389
{
390
    if (s->reg[I_(UCON)] & 0x80) {
391
        uint32_t timeout = ((s->reg[I_(UCON)] >> 12) & 0x0f) * s->wordtime;
392

393
        timer_mod(s->fifo_timeout_timer,
394
                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout);
395
    } else {
396
        timer_del(s->fifo_timeout_timer);
397
    }
398
}
399

400
static void exynos4210_uart_write(void *opaque, hwaddr offset,
401
                               uint64_t val, unsigned size)
402
{
403
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
404
    uint8_t ch;
405

406
    trace_exynos_uart_write(s->channel, offset,
407
                            exynos4210_uart_regname(offset), val);
408

409
    switch (offset) {
410
    case ULCON:
411
    case UBRDIV:
412
    case UFRACVAL:
413
        s->reg[I_(offset)] = val;
414
        exynos4210_uart_update_parameters(s);
415
        break;
416
    case UFCON:
417
        s->reg[I_(UFCON)] = val;
418
        if (val & UFCON_Rx_FIFO_RESET) {
419
            fifo_reset(&s->rx);
420
            s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET;
421
            trace_exynos_uart_rx_fifo_reset(s->channel);
422
        }
423
        if (val & UFCON_Tx_FIFO_RESET) {
424
            fifo_reset(&s->tx);
425
            s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET;
426
            trace_exynos_uart_tx_fifo_reset(s->channel);
427
        }
428
        break;
429

430
    case UTXH:
431
        if (qemu_chr_fe_backend_connected(&s->chr)) {
432
            s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
433
                    UTRSTAT_Tx_BUFFER_EMPTY);
434
            ch = (uint8_t)val;
435
            /* XXX this blocks entire thread. Rewrite to use
436
             * qemu_chr_fe_write and background I/O callbacks */
437
            qemu_chr_fe_write_all(&s->chr, &ch, 1);
438
            trace_exynos_uart_tx(s->channel, ch);
439
            s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY |
440
                    UTRSTAT_Tx_BUFFER_EMPTY;
441
            s->reg[I_(UINTSP)]  |= UINTSP_TXD;
442
            exynos4210_uart_update_irq(s);
443
        }
444
        break;
445

446
    case UINTP:
447
        s->reg[I_(UINTP)] &= ~val;
448
        s->reg[I_(UINTSP)] &= ~val;
449
        trace_exynos_uart_intclr(s->channel, s->reg[I_(UINTP)]);
450
        exynos4210_uart_update_irq(s);
451
        break;
452
    case UTRSTAT:
453
        if (val & UTRSTAT_Rx_TIMEOUT) {
454
            s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_TIMEOUT;
455
        }
456
        break;
457
    case UERSTAT:
458
    case UFSTAT:
459
    case UMSTAT:
460
    case URXH:
461
        trace_exynos_uart_ro_write(
462
                    s->channel, exynos4210_uart_regname(offset), offset);
463
        break;
464
    case UINTSP:
465
        s->reg[I_(UINTSP)]  &= ~val;
466
        break;
467
    case UINTM:
468
        s->reg[I_(UINTM)] = val;
469
        exynos4210_uart_update_irq(s);
470
        break;
471
    case UCON:
472
    case UMCON:
473
    default:
474
        s->reg[I_(offset)] = val;
475
        break;
476
    }
477
}
478

479
static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset,
480
                                  unsigned size)
481
{
482
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
483
    uint32_t res;
484

485
    switch (offset) {
486
    case UERSTAT: /* Read Only */
487
        res = s->reg[I_(UERSTAT)];
488
        s->reg[I_(UERSTAT)] = 0;
489
        trace_exynos_uart_read(s->channel, offset,
490
                               exynos4210_uart_regname(offset), res);
491
        return res;
492
    case UFSTAT: /* Read Only */
493
        s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
494
        if (fifo_empty_elements_number(&s->rx) == 0) {
495
            s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL;
496
            s->reg[I_(UFSTAT)] &= ~0xff;
497
        }
498
        trace_exynos_uart_read(s->channel, offset,
499
                               exynos4210_uart_regname(offset),
500
                               s->reg[I_(UFSTAT)]);
501
        return s->reg[I_(UFSTAT)];
502
    case URXH:
503
        if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
504
            if (fifo_elements_number(&s->rx)) {
505
                res = fifo_retrieve(&s->rx);
506
                trace_exynos_uart_rx(s->channel, res);
507
                if (!fifo_elements_number(&s->rx)) {
508
                    s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
509
                } else {
510
                    s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
511
                }
512
            } else {
513
                trace_exynos_uart_rx_error(s->channel);
514
                s->reg[I_(UINTSP)] |= UINTSP_ERROR;
515
                exynos4210_uart_update_irq(s);
516
                res = 0;
517
            }
518
        } else {
519
            s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
520
            res = s->reg[I_(URXH)];
521
        }
522
        qemu_chr_fe_accept_input(&s->chr);
523
        exynos4210_uart_update_dmabusy(s);
524
        trace_exynos_uart_read(s->channel, offset,
525
                               exynos4210_uart_regname(offset), res);
526
        return res;
527
    case UTXH:
528
        trace_exynos_uart_wo_read(s->channel, exynos4210_uart_regname(offset),
529
                                  offset);
530
        break;
531
    default:
532
        trace_exynos_uart_read(s->channel, offset,
533
                               exynos4210_uart_regname(offset),
534
                               s->reg[I_(offset)]);
535
        return s->reg[I_(offset)];
536
    }
537

538
    trace_exynos_uart_read(s->channel, offset, exynos4210_uart_regname(offset),
539
                           0);
540
    return 0;
541
}
542

543
static const MemoryRegionOps exynos4210_uart_ops = {
544
    .read = exynos4210_uart_read,
545
    .write = exynos4210_uart_write,
546
    .endianness = DEVICE_NATIVE_ENDIAN,
547
    .valid = {
548
        .max_access_size = 4,
549
        .unaligned = false
550
    },
551
};
552

553
static int exynos4210_uart_can_receive(void *opaque)
554
{
555
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
556

557
    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
558
        return fifo_empty_elements_number(&s->rx);
559
    } else {
560
        return !(s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY);
561
    }
562
}
563

564
static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size)
565
{
566
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
567
    int i;
568

569
    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
570
        if (fifo_empty_elements_number(&s->rx) < size) {
571
            size = fifo_empty_elements_number(&s->rx);
572
            s->reg[I_(UINTSP)] |= UINTSP_ERROR;
573
        }
574
        for (i = 0; i < size; i++) {
575
            fifo_store(&s->rx, buf[i]);
576
        }
577
        exynos4210_uart_rx_timeout_set(s);
578
    } else {
579
        s->reg[I_(URXH)] = buf[0];
580
    }
581
    s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
582

583
    exynos4210_uart_update_irq(s);
584
}
585

586

587
static void exynos4210_uart_event(void *opaque, QEMUChrEvent event)
588
{
589
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
590

591
    if (event == CHR_EVENT_BREAK) {
592
        /* When the RxDn is held in logic 0, then a null byte is pushed into the
593
         * fifo */
594
        fifo_store(&s->rx, '\0');
595
        s->reg[I_(UERSTAT)] |= UERSTAT_BREAK;
596
        exynos4210_uart_update_irq(s);
597
    }
598
}
599

600

601
static void exynos4210_uart_reset(DeviceState *dev)
602
{
603
    Exynos4210UartState *s = EXYNOS4210_UART(dev);
604
    int i;
605

606
    for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) {
607
        s->reg[I_(exynos4210_uart_regs[i].offset)] =
608
                exynos4210_uart_regs[i].reset_value;
609
    }
610

611
    fifo_reset(&s->rx);
612
    fifo_reset(&s->tx);
613

614
    trace_exynos_uart_rxsize(s->channel, s->rx.size);
615
}
616

617
static int exynos4210_uart_post_load(void *opaque, int version_id)
618
{
619
    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
620

621
    exynos4210_uart_update_parameters(s);
622
    exynos4210_uart_rx_timeout_set(s);
623

624
    return 0;
625
}
626

627
static const VMStateDescription vmstate_exynos4210_uart_fifo = {
628
    .name = "exynos4210.uart.fifo",
629
    .version_id = 1,
630
    .minimum_version_id = 1,
631
    .fields = (const VMStateField[]) {
632
        VMSTATE_UINT32(sp, Exynos4210UartFIFO),
633
        VMSTATE_UINT32(rp, Exynos4210UartFIFO),
634
        VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, size),
635
        VMSTATE_END_OF_LIST()
636
    }
637
};
638

639
static const VMStateDescription vmstate_exynos4210_uart = {
640
    .name = "exynos4210.uart",
641
    .version_id = 1,
642
    .minimum_version_id = 1,
643
    .post_load = exynos4210_uart_post_load,
644
    .fields = (const VMStateField[]) {
645
        VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
646
                       vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
647
        VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState,
648
                             EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
649
        VMSTATE_END_OF_LIST()
650
    }
651
};
652

653
DeviceState *exynos4210_uart_create(hwaddr addr,
654
                                    int fifo_size,
655
                                    int channel,
656
                                    Chardev *chr,
657
                                    qemu_irq irq)
658
{
659
    DeviceState  *dev;
660
    SysBusDevice *bus;
661

662
    dev = qdev_new(TYPE_EXYNOS4210_UART);
663

664
    qdev_prop_set_chr(dev, "chardev", chr);
665
    qdev_prop_set_uint32(dev, "channel", channel);
666
    qdev_prop_set_uint32(dev, "rx-size", fifo_size);
667
    qdev_prop_set_uint32(dev, "tx-size", fifo_size);
668

669
    bus = SYS_BUS_DEVICE(dev);
670
    sysbus_realize_and_unref(bus, &error_fatal);
671
    if (addr != (hwaddr)-1) {
672
        sysbus_mmio_map(bus, 0, addr);
673
    }
674
    sysbus_connect_irq(bus, 0, irq);
675

676
    return dev;
677
}
678

679
static void exynos4210_uart_init(Object *obj)
680
{
681
    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
682
    Exynos4210UartState *s = EXYNOS4210_UART(dev);
683

684
    s->wordtime = NANOSECONDS_PER_SECOND * 10 / 9600;
685

686
    /* memory mapping */
687
    memory_region_init_io(&s->iomem, obj, &exynos4210_uart_ops, s,
688
                          "exynos4210.uart", EXYNOS4210_UART_REGS_MEM_SIZE);
689
    sysbus_init_mmio(dev, &s->iomem);
690

691
    sysbus_init_irq(dev, &s->irq);
692
    sysbus_init_irq(dev, &s->dmairq);
693
}
694

695
static void exynos4210_uart_realize(DeviceState *dev, Error **errp)
696
{
697
    Exynos4210UartState *s = EXYNOS4210_UART(dev);
698

699
    s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
700
                                         exynos4210_uart_timeout_int, s);
701

702
    qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive,
703
                             exynos4210_uart_receive, exynos4210_uart_event,
704
                             NULL, s, NULL, true);
705
}
706

707
static Property exynos4210_uart_properties[] = {
708
    DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
709
    DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
710
    DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16),
711
    DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16),
712
    DEFINE_PROP_END_OF_LIST(),
713
};
714

715
static void exynos4210_uart_class_init(ObjectClass *klass, void *data)
716
{
717
    DeviceClass *dc = DEVICE_CLASS(klass);
718

719
    dc->realize = exynos4210_uart_realize;
720
    dc->reset = exynos4210_uart_reset;
721
    device_class_set_props(dc, exynos4210_uart_properties);
722
    dc->vmsd = &vmstate_exynos4210_uart;
723
}
724

725
static const TypeInfo exynos4210_uart_info = {
726
    .name          = TYPE_EXYNOS4210_UART,
727
    .parent        = TYPE_SYS_BUS_DEVICE,
728
    .instance_size = sizeof(Exynos4210UartState),
729
    .instance_init = exynos4210_uart_init,
730
    .class_init    = exynos4210_uart_class_init,
731
};
732

733
static void exynos4210_uart_register(void)
734
{
735
    type_register_static(&exynos4210_uart_info);
736
}
737

738
type_init(exynos4210_uart_register)
739

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.