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