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