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