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