qemu/hw/char/sh_serial.c
<<
>>
Prefs
   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 "qemu/osdep.h"
  28#include "hw/hw.h"
  29#include "hw/sh4/sh.h"
  30#include "chardev/char-fe.h"
  31#include "qapi/error.h"
  32#include "qemu/timer.h"
  33
  34//#define DEBUG_SERIAL
  35
  36#define SH_SERIAL_FLAG_TEND (1 << 0)
  37#define SH_SERIAL_FLAG_TDE  (1 << 1)
  38#define SH_SERIAL_FLAG_RDF  (1 << 2)
  39#define SH_SERIAL_FLAG_BRK  (1 << 3)
  40#define SH_SERIAL_FLAG_DR   (1 << 4)
  41
  42#define SH_RX_FIFO_LENGTH (16)
  43
  44typedef struct {
  45    MemoryRegion iomem;
  46    MemoryRegion iomem_p4;
  47    MemoryRegion iomem_a7;
  48    uint8_t smr;
  49    uint8_t brr;
  50    uint8_t scr;
  51    uint8_t dr; /* ftdr / tdr */
  52    uint8_t sr; /* fsr / ssr */
  53    uint16_t fcr;
  54    uint8_t sptr;
  55
  56    uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
  57    uint8_t rx_cnt;
  58    uint8_t rx_tail;
  59    uint8_t rx_head;
  60
  61    int freq;
  62    int feat;
  63    int flags;
  64    int rtrg;
  65
  66    CharBackend chr;
  67    QEMUTimer *fifo_timeout_timer;
  68    uint64_t etu; /* Elementary Time Unit (ns) */
  69
  70    qemu_irq eri;
  71    qemu_irq rxi;
  72    qemu_irq txi;
  73    qemu_irq tei;
  74    qemu_irq bri;
  75} sh_serial_state;
  76
  77static void sh_serial_clear_fifo(sh_serial_state * s)
  78{
  79    memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
  80    s->rx_cnt = 0;
  81    s->rx_head = 0;
  82    s->rx_tail = 0;
  83}
  84
  85static void sh_serial_write(void *opaque, hwaddr offs,
  86                            uint64_t val, unsigned size)
  87{
  88    sh_serial_state *s = opaque;
  89    unsigned char ch;
  90
  91#ifdef DEBUG_SERIAL
  92    printf("sh_serial: write offs=0x%02x val=0x%02x\n",
  93           offs, val);
  94#endif
  95    switch(offs) {
  96    case 0x00: /* SMR */
  97        s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
  98        return;
  99    case 0x04: /* BRR */
 100        s->brr = val;
 101        return;
 102    case 0x08: /* SCR */
 103        /* TODO : For SH7751, SCIF mask should be 0xfb. */
 104        s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
 105        if (!(val & (1 << 5)))
 106            s->flags |= SH_SERIAL_FLAG_TEND;
 107        if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
 108            qemu_set_irq(s->txi, val & (1 << 7));
 109        }
 110        if (!(val & (1 << 6))) {
 111            qemu_set_irq(s->rxi, 0);
 112        }
 113        return;
 114    case 0x0c: /* FTDR / TDR */
 115        if (qemu_chr_fe_backend_connected(&s->chr)) {
 116            ch = val;
 117            /* XXX this blocks entire thread. Rewrite to use
 118             * qemu_chr_fe_write and background I/O callbacks */
 119            qemu_chr_fe_write_all(&s->chr, &ch, 1);
 120        }
 121        s->dr = val;
 122        s->flags &= ~SH_SERIAL_FLAG_TDE;
 123        return;
 124#if 0
 125    case 0x14: /* FRDR / RDR */
 126        ret = 0;
 127        break;
 128#endif
 129    }
 130    if (s->feat & SH_SERIAL_FEAT_SCIF) {
 131        switch(offs) {
 132        case 0x10: /* FSR */
 133            if (!(val & (1 << 6)))
 134                s->flags &= ~SH_SERIAL_FLAG_TEND;
 135            if (!(val & (1 << 5)))
 136                s->flags &= ~SH_SERIAL_FLAG_TDE;
 137            if (!(val & (1 << 4)))
 138                s->flags &= ~SH_SERIAL_FLAG_BRK;
 139            if (!(val & (1 << 1)))
 140                s->flags &= ~SH_SERIAL_FLAG_RDF;
 141            if (!(val & (1 << 0)))
 142                s->flags &= ~SH_SERIAL_FLAG_DR;
 143
 144            if (!(val & (1 << 1)) || !(val & (1 << 0))) {
 145                if (s->rxi) {
 146                    qemu_set_irq(s->rxi, 0);
 147                }
 148            }
 149            return;
 150        case 0x18: /* FCR */
 151            s->fcr = val;
 152            switch ((val >> 6) & 3) {
 153            case 0:
 154                s->rtrg = 1;
 155                break;
 156            case 1:
 157                s->rtrg = 4;
 158                break;
 159            case 2:
 160                s->rtrg = 8;
 161                break;
 162            case 3:
 163                s->rtrg = 14;
 164                break;
 165            }
 166            if (val & (1 << 1)) {
 167                sh_serial_clear_fifo(s);
 168                s->sr &= ~(1 << 1);
 169            }
 170
 171            return;
 172        case 0x20: /* SPTR */
 173            s->sptr = val & 0xf3;
 174            return;
 175        case 0x24: /* LSR */
 176            return;
 177        }
 178    }
 179    else {
 180        switch(offs) {
 181#if 0
 182        case 0x0c:
 183            ret = s->dr;
 184            break;
 185        case 0x10:
 186            ret = 0;
 187            break;
 188#endif
 189        case 0x1c:
 190            s->sptr = val & 0x8f;
 191            return;
 192        }
 193    }
 194
 195    fprintf(stderr, "sh_serial: unsupported write to 0x%02"
 196            HWADDR_PRIx "\n", offs);
 197    abort();
 198}
 199
 200static uint64_t sh_serial_read(void *opaque, hwaddr offs,
 201                               unsigned size)
 202{
 203    sh_serial_state *s = opaque;
 204    uint32_t ret = ~0;
 205
 206#if 0
 207    switch(offs) {
 208    case 0x00:
 209        ret = s->smr;
 210        break;
 211    case 0x04:
 212        ret = s->brr;
 213        break;
 214    case 0x08:
 215        ret = s->scr;
 216        break;
 217    case 0x14:
 218        ret = 0;
 219        break;
 220    }
 221#endif
 222    if (s->feat & SH_SERIAL_FEAT_SCIF) {
 223        switch(offs) {
 224        case 0x00: /* SMR */
 225            ret = s->smr;
 226            break;
 227        case 0x08: /* SCR */
 228            ret = s->scr;
 229            break;
 230        case 0x10: /* FSR */
 231            ret = 0;
 232            if (s->flags & SH_SERIAL_FLAG_TEND)
 233                ret |= (1 << 6);
 234            if (s->flags & SH_SERIAL_FLAG_TDE)
 235                ret |= (1 << 5);
 236            if (s->flags & SH_SERIAL_FLAG_BRK)
 237                ret |= (1 << 4);
 238            if (s->flags & SH_SERIAL_FLAG_RDF)
 239                ret |= (1 << 1);
 240            if (s->flags & SH_SERIAL_FLAG_DR)
 241                ret |= (1 << 0);
 242
 243            if (s->scr & (1 << 5))
 244                s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
 245
 246            break;
 247        case 0x14:
 248            if (s->rx_cnt > 0) {
 249                ret = s->rx_fifo[s->rx_tail++];
 250                s->rx_cnt--;
 251                if (s->rx_tail == SH_RX_FIFO_LENGTH)
 252                    s->rx_tail = 0;
 253                if (s->rx_cnt < s->rtrg)
 254                    s->flags &= ~SH_SERIAL_FLAG_RDF;
 255            }
 256            break;
 257        case 0x18:
 258            ret = s->fcr;
 259            break;
 260        case 0x1c:
 261            ret = s->rx_cnt;
 262            break;
 263        case 0x20:
 264            ret = s->sptr;
 265            break;
 266        case 0x24:
 267            ret = 0;
 268            break;
 269        }
 270    }
 271    else {
 272        switch(offs) {
 273#if 0
 274        case 0x0c:
 275            ret = s->dr;
 276            break;
 277        case 0x10:
 278            ret = 0;
 279            break;
 280        case 0x14:
 281            ret = s->rx_fifo[0];
 282            break;
 283#endif
 284        case 0x1c:
 285            ret = s->sptr;
 286            break;
 287        }
 288    }
 289#ifdef DEBUG_SERIAL
 290    printf("sh_serial: read offs=0x%02x val=0x%x\n",
 291           offs, ret);
 292#endif
 293
 294    if (ret & ~((1 << 16) - 1)) {
 295        fprintf(stderr, "sh_serial: unsupported read from 0x%02"
 296                HWADDR_PRIx "\n", offs);
 297        abort();
 298    }
 299
 300    return ret;
 301}
 302
 303static int sh_serial_can_receive(sh_serial_state *s)
 304{
 305    return s->scr & (1 << 4);
 306}
 307
 308static void sh_serial_receive_break(sh_serial_state *s)
 309{
 310    if (s->feat & SH_SERIAL_FEAT_SCIF)
 311        s->sr |= (1 << 4);
 312}
 313
 314static int sh_serial_can_receive1(void *opaque)
 315{
 316    sh_serial_state *s = opaque;
 317    return sh_serial_can_receive(s);
 318}
 319
 320static void sh_serial_timeout_int(void *opaque)
 321{
 322    sh_serial_state *s = opaque;
 323
 324    s->flags |= SH_SERIAL_FLAG_RDF;
 325    if (s->scr & (1 << 6) && s->rxi) {
 326        qemu_set_irq(s->rxi, 1);
 327    }
 328}
 329
 330static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
 331{
 332    sh_serial_state *s = opaque;
 333
 334    if (s->feat & SH_SERIAL_FEAT_SCIF) {
 335        int i;
 336        for (i = 0; i < size; i++) {
 337            if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
 338                s->rx_fifo[s->rx_head++] = buf[i];
 339                if (s->rx_head == SH_RX_FIFO_LENGTH) {
 340                    s->rx_head = 0;
 341                }
 342                s->rx_cnt++;
 343                if (s->rx_cnt >= s->rtrg) {
 344                    s->flags |= SH_SERIAL_FLAG_RDF;
 345                    if (s->scr & (1 << 6) && s->rxi) {
 346                        timer_del(s->fifo_timeout_timer);
 347                        qemu_set_irq(s->rxi, 1);
 348                    }
 349                } else {
 350                    timer_mod(s->fifo_timeout_timer,
 351                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
 352                }
 353            }
 354        }
 355    } else {
 356        s->rx_fifo[0] = buf[0];
 357    }
 358}
 359
 360static void sh_serial_event(void *opaque, int event)
 361{
 362    sh_serial_state *s = opaque;
 363    if (event == CHR_EVENT_BREAK)
 364        sh_serial_receive_break(s);
 365}
 366
 367static const MemoryRegionOps sh_serial_ops = {
 368    .read = sh_serial_read,
 369    .write = sh_serial_write,
 370    .endianness = DEVICE_NATIVE_ENDIAN,
 371};
 372
 373void sh_serial_init(MemoryRegion *sysmem,
 374                    hwaddr base, int feat,
 375                    uint32_t freq, Chardev *chr,
 376                    qemu_irq eri_source,
 377                    qemu_irq rxi_source,
 378                    qemu_irq txi_source,
 379                    qemu_irq tei_source,
 380                    qemu_irq bri_source)
 381{
 382    sh_serial_state *s;
 383
 384    s = g_malloc0(sizeof(sh_serial_state));
 385
 386    s->feat = feat;
 387    s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
 388    s->rtrg = 1;
 389
 390    s->smr = 0;
 391    s->brr = 0xff;
 392    s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
 393    s->sptr = 0;
 394
 395    if (feat & SH_SERIAL_FEAT_SCIF) {
 396        s->fcr = 0;
 397    }
 398    else {
 399        s->dr = 0xff;
 400    }
 401
 402    sh_serial_clear_fifo(s);
 403
 404    memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s,
 405                          "serial", 0x100000000ULL);
 406
 407    memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem,
 408                             0, 0x28);
 409    memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
 410
 411    memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem,
 412                             0, 0x28);
 413    memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
 414
 415    if (chr) {
 416        qemu_chr_fe_init(&s->chr, chr, &error_abort);
 417        qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
 418                                 sh_serial_receive1,
 419                                 sh_serial_event, NULL, s, NULL, true);
 420    }
 421
 422    s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
 423                                         sh_serial_timeout_int, s);
 424    s->etu = NANOSECONDS_PER_SECOND / 9600;
 425    s->eri = eri_source;
 426    s->rxi = rxi_source;
 427    s->txi = txi_source;
 428    s->tei = tei_source;
 429    s->bri = bri_source;
 430}
 431