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