qemu/hw/exynos4210_uart.c
<<
>>
Prefs
   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 "sysbus.h"
  23#include "sysemu/sysemu.h"
  24#include "char/char.h"
  25
  26#include "exynos4210.h"
  27
  28#undef DEBUG_UART
  29#undef DEBUG_UART_EXTEND
  30#undef DEBUG_IRQ
  31#undef DEBUG_Rx_DATA
  32#undef DEBUG_Tx_DATA
  33
  34#define DEBUG_UART            0
  35#define DEBUG_UART_EXTEND     0
  36#define DEBUG_IRQ             0
  37#define DEBUG_Rx_DATA         0
  38#define DEBUG_Tx_DATA         0
  39
  40#if DEBUG_UART
  41#define  PRINT_DEBUG(fmt, args...)  \
  42        do { \
  43            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
  44        } while (0)
  45
  46#if DEBUG_UART_EXTEND
  47#define  PRINT_DEBUG_EXTEND(fmt, args...) \
  48        do { \
  49            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
  50        } while (0)
  51#else
  52#define  PRINT_DEBUG_EXTEND(fmt, args...) \
  53        do {} while (0)
  54#endif /* EXTEND */
  55
  56#else
  57#define  PRINT_DEBUG(fmt, args...)  \
  58        do {} while (0)
  59#define  PRINT_DEBUG_EXTEND(fmt, args...) \
  60        do {} while (0)
  61#endif
  62
  63#define  PRINT_ERROR(fmt, args...) \
  64        do { \
  65            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
  66        } while (0)
  67
  68/*
  69 *  Offsets for UART registers relative to SFR base address
  70 *  for UARTn
  71 *
  72 */
  73#define ULCON      0x0000 /* Line Control             */
  74#define UCON       0x0004 /* Control                  */
  75#define UFCON      0x0008 /* FIFO Control             */
  76#define UMCON      0x000C /* Modem Control            */
  77#define UTRSTAT    0x0010 /* Tx/Rx Status             */
  78#define UERSTAT    0x0014 /* UART Error Status        */
  79#define UFSTAT     0x0018 /* FIFO Status              */
  80#define UMSTAT     0x001C /* Modem Status             */
  81#define UTXH       0x0020 /* Transmit Buffer          */
  82#define URXH       0x0024 /* Receive Buffer           */
  83#define UBRDIV     0x0028 /* Baud Rate Divisor        */
  84#define UFRACVAL   0x002C /* Divisor Fractional Value */
  85#define UINTP      0x0030 /* Interrupt Pending        */
  86#define UINTSP     0x0034 /* Interrupt Source Pending */
  87#define UINTM      0x0038 /* Interrupt Mask           */
  88
  89/*
  90 * for indexing register in the uint32_t array
  91 *
  92 * 'reg' - register offset (see offsets definitions above)
  93 *
  94 */
  95#define I_(reg) (reg / sizeof(uint32_t))
  96
  97typedef struct Exynos4210UartReg {
  98    const char         *name; /* the only reason is the debug output */
  99    hwaddr  offset;
 100    uint32_t            reset_value;
 101} Exynos4210UartReg;
 102
 103static Exynos4210UartReg exynos4210_uart_regs[] = {
 104    {"ULCON",    ULCON,    0x00000000},
 105    {"UCON",     UCON,     0x00003000},
 106    {"UFCON",    UFCON,    0x00000000},
 107    {"UMCON",    UMCON,    0x00000000},
 108    {"UTRSTAT",  UTRSTAT,  0x00000006}, /* RO */
 109    {"UERSTAT",  UERSTAT,  0x00000000}, /* RO */
 110    {"UFSTAT",   UFSTAT,   0x00000000}, /* RO */
 111    {"UMSTAT",   UMSTAT,   0x00000000}, /* RO */
 112    {"UTXH",     UTXH,     0x5c5c5c5c}, /* WO, undefined reset value*/
 113    {"URXH",     URXH,     0x00000000}, /* RO */
 114    {"UBRDIV",   UBRDIV,   0x00000000},
 115    {"UFRACVAL", UFRACVAL, 0x00000000},
 116    {"UINTP",    UINTP,    0x00000000},
 117    {"UINTSP",   UINTSP,   0x00000000},
 118    {"UINTM",    UINTM,    0x00000000},
 119};
 120
 121#define EXYNOS4210_UART_REGS_MEM_SIZE    0x3C
 122
 123/* UART FIFO Control */
 124#define UFCON_FIFO_ENABLE                    0x1
 125#define UFCON_Rx_FIFO_RESET                  0x2
 126#define UFCON_Tx_FIFO_RESET                  0x4
 127#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT    8
 128#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT)
 129#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT    4
 130#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT)
 131
 132/* Uart FIFO Status */
 133#define UFSTAT_Rx_FIFO_COUNT        0xff
 134#define UFSTAT_Rx_FIFO_FULL         0x100
 135#define UFSTAT_Rx_FIFO_ERROR        0x200
 136#define UFSTAT_Tx_FIFO_COUNT_SHIFT  16
 137#define UFSTAT_Tx_FIFO_COUNT        (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT)
 138#define UFSTAT_Tx_FIFO_FULL_SHIFT   24
 139#define UFSTAT_Tx_FIFO_FULL         (1 << UFSTAT_Tx_FIFO_FULL_SHIFT)
 140
 141/* UART Interrupt Source Pending */
 142#define UINTSP_RXD      0x1 /* Receive interrupt  */
 143#define UINTSP_ERROR    0x2 /* Error interrupt    */
 144#define UINTSP_TXD      0x4 /* Transmit interrupt */
 145#define UINTSP_MODEM    0x8 /* Modem interrupt    */
 146
 147/* UART Line Control */
 148#define ULCON_IR_MODE_SHIFT   6
 149#define ULCON_PARITY_SHIFT    3
 150#define ULCON_STOP_BIT_SHIFT  1
 151
 152/* UART Tx/Rx Status */
 153#define UTRSTAT_TRANSMITTER_EMPTY       0x4
 154#define UTRSTAT_Tx_BUFFER_EMPTY         0x2
 155#define UTRSTAT_Rx_BUFFER_DATA_READY    0x1
 156
 157/* UART Error Status */
 158#define UERSTAT_OVERRUN  0x1
 159#define UERSTAT_PARITY   0x2
 160#define UERSTAT_FRAME    0x4
 161#define UERSTAT_BREAK    0x8
 162
 163typedef struct {
 164    uint8_t    *data;
 165    uint32_t    sp, rp; /* store and retrieve pointers */
 166    uint32_t    size;
 167} Exynos4210UartFIFO;
 168
 169typedef struct {
 170    SysBusDevice busdev;
 171    MemoryRegion iomem;
 172
 173    uint32_t             reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
 174    Exynos4210UartFIFO   rx;
 175    Exynos4210UartFIFO   tx;
 176
 177    CharDriverState  *chr;
 178    qemu_irq          irq;
 179
 180    uint32_t channel;
 181
 182} Exynos4210UartState;
 183
 184
 185#if DEBUG_UART
 186/* Used only for debugging inside PRINT_DEBUG_... macros */
 187static const char *exynos4210_uart_regname(hwaddr  offset)
 188{
 189
 190    int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg);
 191    int i;
 192
 193    for (i = 0; i < regs_number; i++) {
 194        if (offset == exynos4210_uart_regs[i].offset) {
 195            return exynos4210_uart_regs[i].name;
 196        }
 197    }
 198
 199    return NULL;
 200}
 201#endif
 202
 203
 204static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch)
 205{
 206    q->data[q->sp] = ch;
 207    q->sp = (q->sp + 1) % q->size;
 208}
 209
 210static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
 211{
 212    uint8_t ret = q->data[q->rp];
 213    q->rp = (q->rp + 1) % q->size;
 214    return  ret;
 215}
 216
 217static int fifo_elements_number(Exynos4210UartFIFO *q)
 218{
 219    if (q->sp < q->rp) {
 220        return q->size - q->rp + q->sp;
 221    }
 222
 223    return q->sp - q->rp;
 224}
 225
 226static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
 227{
 228    return q->size - fifo_elements_number(q);
 229}
 230
 231static void fifo_reset(Exynos4210UartFIFO *q)
 232{
 233    if (q->data != NULL) {
 234        g_free(q->data);
 235        q->data = NULL;
 236    }
 237
 238    q->data = (uint8_t *)g_malloc0(q->size);
 239
 240    q->sp = 0;
 241    q->rp = 0;
 242}
 243
 244static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s)
 245{
 246    uint32_t level = 0;
 247    uint32_t reg;
 248
 249    reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >>
 250            UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT;
 251
 252    switch (s->channel) {
 253    case 0:
 254        level = reg * 32;
 255        break;
 256    case 1:
 257    case 4:
 258        level = reg * 8;
 259        break;
 260    case 2:
 261    case 3:
 262        level = reg * 2;
 263        break;
 264    default:
 265        level = 0;
 266        PRINT_ERROR("Wrong UART channel number: %d\n", s->channel);
 267    }
 268
 269    return level;
 270}
 271
 272static void exynos4210_uart_update_irq(Exynos4210UartState *s)
 273{
 274    /*
 275     * The Tx interrupt is always requested if the number of data in the
 276     * transmit FIFO is smaller than the trigger level.
 277     */
 278    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
 279
 280        uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >>
 281                UFSTAT_Tx_FIFO_COUNT_SHIFT;
 282
 283        if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) {
 284            s->reg[I_(UINTSP)] |= UINTSP_TXD;
 285        }
 286    }
 287
 288    s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)];
 289
 290    if (s->reg[I_(UINTP)]) {
 291        qemu_irq_raise(s->irq);
 292
 293#if DEBUG_IRQ
 294        fprintf(stderr, "UART%d: IRQ has been raised: %08x\n",
 295                s->channel, s->reg[I_(UINTP)]);
 296#endif
 297
 298    } else {
 299        qemu_irq_lower(s->irq);
 300    }
 301}
 302
 303static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
 304{
 305    int speed, parity, data_bits, stop_bits, frame_size;
 306    QEMUSerialSetParams ssp;
 307    uint64_t uclk_rate;
 308
 309    if (s->reg[I_(UBRDIV)] == 0) {
 310        return;
 311    }
 312
 313    frame_size = 1; /* start bit */
 314    if (s->reg[I_(ULCON)] & 0x20) {
 315        frame_size++; /* parity bit */
 316        if (s->reg[I_(ULCON)] & 0x28) {
 317            parity = 'E';
 318        } else {
 319            parity = 'O';
 320        }
 321    } else {
 322        parity = 'N';
 323    }
 324
 325    if (s->reg[I_(ULCON)] & 0x4) {
 326        stop_bits = 2;
 327    } else {
 328        stop_bits = 1;
 329    }
 330
 331    data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
 332
 333    frame_size += data_bits + stop_bits;
 334
 335    uclk_rate = 24000000;
 336
 337    speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
 338            (s->reg[I_(UFRACVAL)] & 0x7) + 16);
 339
 340    ssp.speed     = speed;
 341    ssp.parity    = parity;
 342    ssp.data_bits = data_bits;
 343    ssp.stop_bits = stop_bits;
 344
 345    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 346
 347    PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
 348                s->channel, speed, parity, data_bits, stop_bits);
 349}
 350
 351static void exynos4210_uart_write(void *opaque, hwaddr offset,
 352                               uint64_t val, unsigned size)
 353{
 354    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
 355    uint8_t ch;
 356
 357    PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel,
 358        offset, exynos4210_uart_regname(offset), (long long unsigned int)val);
 359
 360    switch (offset) {
 361    case ULCON:
 362    case UBRDIV:
 363    case UFRACVAL:
 364        s->reg[I_(offset)] = val;
 365        exynos4210_uart_update_parameters(s);
 366        break;
 367    case UFCON:
 368        s->reg[I_(UFCON)] = val;
 369        if (val & UFCON_Rx_FIFO_RESET) {
 370            fifo_reset(&s->rx);
 371            s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET;
 372            PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel);
 373        }
 374        if (val & UFCON_Tx_FIFO_RESET) {
 375            fifo_reset(&s->tx);
 376            s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET;
 377            PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel);
 378        }
 379        break;
 380
 381    case UTXH:
 382        if (s->chr) {
 383            s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
 384                    UTRSTAT_Tx_BUFFER_EMPTY);
 385            ch = (uint8_t)val;
 386            qemu_chr_fe_write(s->chr, &ch, 1);
 387#if DEBUG_Tx_DATA
 388            fprintf(stderr, "%c", ch);
 389#endif
 390            s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY |
 391                    UTRSTAT_Tx_BUFFER_EMPTY;
 392            s->reg[I_(UINTSP)]  |= UINTSP_TXD;
 393            exynos4210_uart_update_irq(s);
 394        }
 395        break;
 396
 397    case UINTP:
 398        s->reg[I_(UINTP)] &= ~val;
 399        s->reg[I_(UINTSP)] &= ~val;
 400        PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
 401                    s->channel, offset, s->reg[I_(UINTP)]);
 402        exynos4210_uart_update_irq(s);
 403        break;
 404    case UTRSTAT:
 405    case UERSTAT:
 406    case UFSTAT:
 407    case UMSTAT:
 408    case URXH:
 409        PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n",
 410                    s->channel, exynos4210_uart_regname(offset), offset);
 411        break;
 412    case UINTSP:
 413        s->reg[I_(UINTSP)]  &= ~val;
 414        break;
 415    case UINTM:
 416        s->reg[I_(UINTM)] = val;
 417        exynos4210_uart_update_irq(s);
 418        break;
 419    case UCON:
 420    case UMCON:
 421    default:
 422        s->reg[I_(offset)] = val;
 423        break;
 424    }
 425}
 426static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset,
 427                                  unsigned size)
 428{
 429    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
 430    uint32_t res;
 431
 432    switch (offset) {
 433    case UERSTAT: /* Read Only */
 434        res = s->reg[I_(UERSTAT)];
 435        s->reg[I_(UERSTAT)] = 0;
 436        return res;
 437    case UFSTAT: /* Read Only */
 438        s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
 439        if (fifo_empty_elements_number(&s->rx) == 0) {
 440            s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL;
 441            s->reg[I_(UFSTAT)] &= ~0xff;
 442        }
 443        return s->reg[I_(UFSTAT)];
 444    case URXH:
 445        if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
 446            if (fifo_elements_number(&s->rx)) {
 447                res = fifo_retrieve(&s->rx);
 448#if DEBUG_Rx_DATA
 449                fprintf(stderr, "%c", res);
 450#endif
 451                if (!fifo_elements_number(&s->rx)) {
 452                    s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
 453                } else {
 454                    s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
 455                }
 456            } else {
 457                s->reg[I_(UINTSP)] |= UINTSP_ERROR;
 458                exynos4210_uart_update_irq(s);
 459                res = 0;
 460            }
 461        } else {
 462            s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
 463            res = s->reg[I_(URXH)];
 464        }
 465        return res;
 466    case UTXH:
 467        PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n",
 468                    s->channel, exynos4210_uart_regname(offset), offset);
 469        break;
 470    default:
 471        return s->reg[I_(offset)];
 472    }
 473
 474    return 0;
 475}
 476
 477static const MemoryRegionOps exynos4210_uart_ops = {
 478    .read = exynos4210_uart_read,
 479    .write = exynos4210_uart_write,
 480    .endianness = DEVICE_NATIVE_ENDIAN,
 481    .valid = {
 482        .max_access_size = 4,
 483        .unaligned = false
 484    },
 485};
 486
 487static int exynos4210_uart_can_receive(void *opaque)
 488{
 489    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
 490
 491    return fifo_empty_elements_number(&s->rx);
 492}
 493
 494
 495static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size)
 496{
 497    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
 498    int i;
 499
 500    if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
 501        if (fifo_empty_elements_number(&s->rx) < size) {
 502            for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) {
 503                fifo_store(&s->rx, buf[i]);
 504            }
 505            s->reg[I_(UINTSP)] |= UINTSP_ERROR;
 506            s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
 507        } else {
 508            for (i = 0; i < size; i++) {
 509                fifo_store(&s->rx, buf[i]);
 510            }
 511            s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
 512        }
 513        /* XXX: Around here we maybe should check Rx trigger level */
 514        s->reg[I_(UINTSP)] |= UINTSP_RXD;
 515    } else {
 516        s->reg[I_(URXH)] = buf[0];
 517        s->reg[I_(UINTSP)] |= UINTSP_RXD;
 518        s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
 519    }
 520
 521    exynos4210_uart_update_irq(s);
 522}
 523
 524
 525static void exynos4210_uart_event(void *opaque, int event)
 526{
 527    Exynos4210UartState *s = (Exynos4210UartState *)opaque;
 528
 529    if (event == CHR_EVENT_BREAK) {
 530        /* When the RxDn is held in logic 0, then a null byte is pushed into the
 531         * fifo */
 532        fifo_store(&s->rx, '\0');
 533        s->reg[I_(UERSTAT)] |= UERSTAT_BREAK;
 534        exynos4210_uart_update_irq(s);
 535    }
 536}
 537
 538
 539static void exynos4210_uart_reset(DeviceState *dev)
 540{
 541    Exynos4210UartState *s =
 542            container_of(dev, Exynos4210UartState, busdev.qdev);
 543    int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
 544    int i;
 545
 546    for (i = 0; i < regs_number; i++) {
 547        s->reg[I_(exynos4210_uart_regs[i].offset)] =
 548                exynos4210_uart_regs[i].reset_value;
 549    }
 550
 551    fifo_reset(&s->rx);
 552    fifo_reset(&s->tx);
 553
 554    PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
 555}
 556
 557static const VMStateDescription vmstate_exynos4210_uart_fifo = {
 558    .name = "exynos4210.uart.fifo",
 559    .version_id = 1,
 560    .minimum_version_id = 1,
 561    .minimum_version_id_old = 1,
 562    .fields = (VMStateField[]) {
 563        VMSTATE_UINT32(sp, Exynos4210UartFIFO),
 564        VMSTATE_UINT32(rp, Exynos4210UartFIFO),
 565        VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size),
 566        VMSTATE_END_OF_LIST()
 567    }
 568};
 569
 570static const VMStateDescription vmstate_exynos4210_uart = {
 571    .name = "exynos4210.uart",
 572    .version_id = 1,
 573    .minimum_version_id = 1,
 574    .minimum_version_id_old = 1,
 575    .fields = (VMStateField[]) {
 576        VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
 577                       vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
 578        VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState,
 579                             EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
 580        VMSTATE_END_OF_LIST()
 581    }
 582};
 583
 584DeviceState *exynos4210_uart_create(hwaddr addr,
 585                                 int fifo_size,
 586                                 int channel,
 587                                 CharDriverState *chr,
 588                                 qemu_irq irq)
 589{
 590    DeviceState  *dev;
 591    SysBusDevice *bus;
 592
 593    const char chr_name[] = "serial";
 594    char label[ARRAY_SIZE(chr_name) + 1];
 595
 596    dev = qdev_create(NULL, "exynos4210.uart");
 597
 598    if (!chr) {
 599        if (channel >= MAX_SERIAL_PORTS) {
 600            hw_error("Only %d serial ports are supported by QEMU.\n",
 601                     MAX_SERIAL_PORTS);
 602        }
 603        chr = serial_hds[channel];
 604        if (!chr) {
 605            snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
 606            chr = qemu_chr_new(label, "null", NULL);
 607            if (!(chr)) {
 608                hw_error("Can't assign serial port to UART%d.\n", channel);
 609            }
 610        }
 611    }
 612
 613    qdev_prop_set_chr(dev, "chardev", chr);
 614    qdev_prop_set_uint32(dev, "channel", channel);
 615    qdev_prop_set_uint32(dev, "rx-size", fifo_size);
 616    qdev_prop_set_uint32(dev, "tx-size", fifo_size);
 617
 618    bus = SYS_BUS_DEVICE(dev);
 619    qdev_init_nofail(dev);
 620    if (addr != (hwaddr)-1) {
 621        sysbus_mmio_map(bus, 0, addr);
 622    }
 623    sysbus_connect_irq(bus, 0, irq);
 624
 625    return dev;
 626}
 627
 628static int exynos4210_uart_init(SysBusDevice *dev)
 629{
 630    Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev);
 631
 632    /* memory mapping */
 633    memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart",
 634                          EXYNOS4210_UART_REGS_MEM_SIZE);
 635    sysbus_init_mmio(dev, &s->iomem);
 636
 637    sysbus_init_irq(dev, &s->irq);
 638
 639    qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive,
 640                          exynos4210_uart_receive, exynos4210_uart_event, s);
 641
 642    return 0;
 643}
 644
 645static Property exynos4210_uart_properties[] = {
 646    DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
 647    DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
 648    DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16),
 649    DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16),
 650    DEFINE_PROP_END_OF_LIST(),
 651};
 652
 653static void exynos4210_uart_class_init(ObjectClass *klass, void *data)
 654{
 655    DeviceClass *dc = DEVICE_CLASS(klass);
 656    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 657
 658    k->init = exynos4210_uart_init;
 659    dc->reset = exynos4210_uart_reset;
 660    dc->props = exynos4210_uart_properties;
 661    dc->vmsd = &vmstate_exynos4210_uart;
 662}
 663
 664static const TypeInfo exynos4210_uart_info = {
 665    .name          = "exynos4210.uart",
 666    .parent        = TYPE_SYS_BUS_DEVICE,
 667    .instance_size = sizeof(Exynos4210UartState),
 668    .class_init    = exynos4210_uart_class_init,
 669};
 670
 671static void exynos4210_uart_register(void)
 672{
 673    type_register_static(&exynos4210_uart_info);
 674}
 675
 676type_init(exynos4210_uart_register)
 677