qemu/hw/dma/i8257.c
<<
>>
Prefs
   1/*
   2 * QEMU DMA emulation
   3 *
   4 * Copyright (c) 2003-2004 Vassili Karpov (malc)
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu/osdep.h"
  25#include "hw/hw.h"
  26#include "hw/isa/isa.h"
  27#include "hw/isa/i8257.h"
  28#include "qemu/main-loop.h"
  29#include "trace.h"
  30
  31#define I8257(obj) \
  32    OBJECT_CHECK(I8257State, (obj), TYPE_I8257)
  33
  34/* #define DEBUG_DMA */
  35
  36#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
  37#ifdef DEBUG_DMA
  38#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
  39#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
  40#else
  41#define linfo(...)
  42#define ldebug(...)
  43#endif
  44
  45#define ADDR 0
  46#define COUNT 1
  47
  48enum {
  49    CMD_MEMORY_TO_MEMORY = 0x01,
  50    CMD_FIXED_ADDRESS    = 0x02,
  51    CMD_BLOCK_CONTROLLER = 0x04,
  52    CMD_COMPRESSED_TIME  = 0x08,
  53    CMD_CYCLIC_PRIORITY  = 0x10,
  54    CMD_EXTENDED_WRITE   = 0x20,
  55    CMD_LOW_DREQ         = 0x40,
  56    CMD_LOW_DACK         = 0x80,
  57    CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
  58    | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
  59    | CMD_LOW_DREQ | CMD_LOW_DACK
  60
  61};
  62
  63static void i8257_dma_run(void *opaque);
  64
  65static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
  66
  67static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data)
  68{
  69    I8257State *d = opaque;
  70    int ichan;
  71
  72    ichan = channels[nport & 7];
  73    if (-1 == ichan) {
  74        dolog ("invalid channel %#x %#x\n", nport, data);
  75        return;
  76    }
  77    d->regs[ichan].page = data;
  78}
  79
  80static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data)
  81{
  82    I8257State *d = opaque;
  83    int ichan;
  84
  85    ichan = channels[nport & 7];
  86    if (-1 == ichan) {
  87        dolog ("invalid channel %#x %#x\n", nport, data);
  88        return;
  89    }
  90    d->regs[ichan].pageh = data;
  91}
  92
  93static uint32_t i8257_read_page(void *opaque, uint32_t nport)
  94{
  95    I8257State *d = opaque;
  96    int ichan;
  97
  98    ichan = channels[nport & 7];
  99    if (-1 == ichan) {
 100        dolog ("invalid channel read %#x\n", nport);
 101        return 0;
 102    }
 103    return d->regs[ichan].page;
 104}
 105
 106static uint32_t i8257_read_pageh(void *opaque, uint32_t nport)
 107{
 108    I8257State *d = opaque;
 109    int ichan;
 110
 111    ichan = channels[nport & 7];
 112    if (-1 == ichan) {
 113        dolog ("invalid channel read %#x\n", nport);
 114        return 0;
 115    }
 116    return d->regs[ichan].pageh;
 117}
 118
 119static inline void i8257_init_chan(I8257State *d, int ichan)
 120{
 121    I8257Regs *r;
 122
 123    r = d->regs + ichan;
 124    r->now[ADDR] = r->base[ADDR] << d->dshift;
 125    r->now[COUNT] = 0;
 126}
 127
 128static inline int i8257_getff(I8257State *d)
 129{
 130    int ff;
 131
 132    ff = d->flip_flop;
 133    d->flip_flop = !ff;
 134    return ff;
 135}
 136
 137static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size)
 138{
 139    I8257State *d = opaque;
 140    int ichan, nreg, iport, ff, val, dir;
 141    I8257Regs *r;
 142
 143    iport = (nport >> d->dshift) & 0x0f;
 144    ichan = iport >> 1;
 145    nreg = iport & 1;
 146    r = d->regs + ichan;
 147
 148    dir = ((r->mode >> 5) & 1) ? -1 : 1;
 149    ff = i8257_getff(d);
 150    if (nreg)
 151        val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
 152    else
 153        val = r->now[ADDR] + r->now[COUNT] * dir;
 154
 155    ldebug ("read_chan %#x -> %d\n", iport, val);
 156    return (val >> (d->dshift + (ff << 3))) & 0xff;
 157}
 158
 159static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data,
 160                             unsigned int size)
 161{
 162    I8257State *d = opaque;
 163    int iport, ichan, nreg;
 164    I8257Regs *r;
 165
 166    iport = (nport >> d->dshift) & 0x0f;
 167    ichan = iport >> 1;
 168    nreg = iport & 1;
 169    r = d->regs + ichan;
 170    if (i8257_getff(d)) {
 171        r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
 172        i8257_init_chan(d, ichan);
 173    } else {
 174        r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
 175    }
 176}
 177
 178static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data,
 179                             unsigned int size)
 180{
 181    I8257State *d = opaque;
 182    int iport, ichan = 0;
 183
 184    iport = (nport >> d->dshift) & 0x0f;
 185    switch (iport) {
 186    case 0x00:                  /* command */
 187        if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
 188            dolog("command %"PRIx64" not supported\n", data);
 189            return;
 190        }
 191        d->command = data;
 192        break;
 193
 194    case 0x01:
 195        ichan = data & 3;
 196        if (data & 4) {
 197            d->status |= 1 << (ichan + 4);
 198        }
 199        else {
 200            d->status &= ~(1 << (ichan + 4));
 201        }
 202        d->status &= ~(1 << ichan);
 203        i8257_dma_run(d);
 204        break;
 205
 206    case 0x02:                  /* single mask */
 207        if (data & 4)
 208            d->mask |= 1 << (data & 3);
 209        else
 210            d->mask &= ~(1 << (data & 3));
 211        i8257_dma_run(d);
 212        break;
 213
 214    case 0x03:                  /* mode */
 215        {
 216            ichan = data & 3;
 217#ifdef DEBUG_DMA
 218            {
 219                int op, ai, dir, opmode;
 220                op = (data >> 2) & 3;
 221                ai = (data >> 4) & 1;
 222                dir = (data >> 5) & 1;
 223                opmode = (data >> 6) & 3;
 224
 225                linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
 226                       ichan, op, ai, dir, opmode);
 227            }
 228#endif
 229            d->regs[ichan].mode = data;
 230            break;
 231        }
 232
 233    case 0x04:                  /* clear flip flop */
 234        d->flip_flop = 0;
 235        break;
 236
 237    case 0x05:                  /* reset */
 238        d->flip_flop = 0;
 239        d->mask = ~0;
 240        d->status = 0;
 241        d->command = 0;
 242        break;
 243
 244    case 0x06:                  /* clear mask for all channels */
 245        d->mask = 0;
 246        i8257_dma_run(d);
 247        break;
 248
 249    case 0x07:                  /* write mask for all channels */
 250        d->mask = data;
 251        i8257_dma_run(d);
 252        break;
 253
 254    default:
 255        dolog ("unknown iport %#x\n", iport);
 256        break;
 257    }
 258
 259#ifdef DEBUG_DMA
 260    if (0xc != iport) {
 261        linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
 262               nport, ichan, data);
 263    }
 264#endif
 265}
 266
 267static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size)
 268{
 269    I8257State *d = opaque;
 270    int iport, val;
 271
 272    iport = (nport >> d->dshift) & 0x0f;
 273    switch (iport) {
 274    case 0x00:                  /* status */
 275        val = d->status;
 276        d->status &= 0xf0;
 277        break;
 278    case 0x01:                  /* mask */
 279        val = d->mask;
 280        break;
 281    default:
 282        val = 0;
 283        break;
 284    }
 285
 286    ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
 287    return val;
 288}
 289
 290static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan)
 291{
 292    I8257State *d = I8257(obj);
 293    return (d->regs[nchan & 3].mode >> 2) & 3;
 294}
 295
 296static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan)
 297{
 298    I8257State *d = I8257(obj);
 299    return (d->regs[nchan & 3].mode >> 4) & 1;
 300}
 301
 302static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan)
 303{
 304    I8257State *d = I8257(obj);
 305    int ichan;
 306
 307    ichan = nchan & 3;
 308    d->status |= 1 << (ichan + 4);
 309    i8257_dma_run(d);
 310}
 311
 312static void i8257_dma_release_DREQ(IsaDma *obj, int nchan)
 313{
 314    I8257State *d = I8257(obj);
 315    int ichan;
 316
 317    ichan = nchan & 3;
 318    d->status &= ~(1 << (ichan + 4));
 319    i8257_dma_run(d);
 320}
 321
 322static void i8257_channel_run(I8257State *d, int ichan)
 323{
 324    int ncont = d->dshift;
 325    int n;
 326    I8257Regs *r = &d->regs[ichan];
 327#ifdef DEBUG_DMA
 328    int dir, opmode;
 329
 330    dir = (r->mode >> 5) & 1;
 331    opmode = (r->mode >> 6) & 3;
 332
 333    if (dir) {
 334        dolog ("DMA in address decrement mode\n");
 335    }
 336    if (opmode != 1) {
 337        dolog ("DMA not in single mode select %#x\n", opmode);
 338    }
 339#endif
 340
 341    n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
 342                             r->now[COUNT], (r->base[COUNT] + 1) << ncont);
 343    r->now[COUNT] = n;
 344    ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
 345    if (n == (r->base[COUNT] + 1) << ncont) {
 346        ldebug("transfer done\n");
 347        d->status |= (1 << ichan);
 348    }
 349}
 350
 351static void i8257_dma_run(void *opaque)
 352{
 353    I8257State *d = opaque;
 354    int ichan;
 355    int rearm = 0;
 356
 357    if (d->running) {
 358        rearm = 1;
 359        goto out;
 360    } else {
 361        d->running = 1;
 362    }
 363
 364    for (ichan = 0; ichan < 4; ichan++) {
 365        int mask;
 366
 367        mask = 1 << ichan;
 368
 369        if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
 370            i8257_channel_run(d, ichan);
 371            rearm = 1;
 372        }
 373    }
 374
 375    d->running = 0;
 376out:
 377    if (rearm) {
 378        qemu_bh_schedule_idle(d->dma_bh);
 379        d->dma_bh_scheduled = true;
 380    }
 381}
 382
 383static void i8257_dma_register_channel(IsaDma *obj, int nchan,
 384                                       IsaDmaTransferHandler transfer_handler,
 385                                       void *opaque)
 386{
 387    I8257State *d = I8257(obj);
 388    I8257Regs *r;
 389    int ichan;
 390
 391    ichan = nchan & 3;
 392
 393    r = d->regs + ichan;
 394    r->transfer_handler = transfer_handler;
 395    r->opaque = opaque;
 396}
 397
 398static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos,
 399                                 int len)
 400{
 401    I8257State *d = I8257(obj);
 402    I8257Regs *r = &d->regs[nchan & 3];
 403    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
 404
 405    if (r->mode & 0x20) {
 406        int i;
 407        uint8_t *p = buf;
 408
 409        cpu_physical_memory_read (addr - pos - len, buf, len);
 410        /* What about 16bit transfers? */
 411        for (i = 0; i < len >> 1; i++) {
 412            uint8_t b = p[len - i - 1];
 413            p[i] = b;
 414        }
 415    }
 416    else
 417        cpu_physical_memory_read (addr + pos, buf, len);
 418
 419    return len;
 420}
 421
 422static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos,
 423                                 int len)
 424{
 425    I8257State *s = I8257(obj);
 426    I8257Regs *r = &s->regs[nchan & 3];
 427    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
 428
 429    if (r->mode & 0x20) {
 430        int i;
 431        uint8_t *p = buf;
 432
 433        cpu_physical_memory_write (addr - pos - len, buf, len);
 434        /* What about 16bit transfers? */
 435        for (i = 0; i < len; i++) {
 436            uint8_t b = p[len - i - 1];
 437            p[i] = b;
 438        }
 439    }
 440    else
 441        cpu_physical_memory_write (addr + pos, buf, len);
 442
 443    return len;
 444}
 445
 446/* request the emulator to transfer a new DMA memory block ASAP (even
 447 * if the idle bottom half would not have exited the iothread yet).
 448 */
 449static void i8257_dma_schedule(IsaDma *obj)
 450{
 451    I8257State *d = I8257(obj);
 452    if (d->dma_bh_scheduled) {
 453        qemu_notify_event();
 454    }
 455}
 456
 457static void i8257_reset(DeviceState *dev)
 458{
 459    I8257State *d = I8257(dev);
 460    i8257_write_cont(d, (0x05 << d->dshift), 0, 1);
 461}
 462
 463static int i8257_phony_handler(void *opaque, int nchan, int dma_pos,
 464                               int dma_len)
 465{
 466    trace_i8257_unregistered_dma(nchan, dma_pos, dma_len);
 467    return dma_pos;
 468}
 469
 470
 471static const MemoryRegionOps channel_io_ops = {
 472    .read = i8257_read_chan,
 473    .write = i8257_write_chan,
 474    .endianness = DEVICE_NATIVE_ENDIAN,
 475    .impl = {
 476        .min_access_size = 1,
 477        .max_access_size = 1,
 478    },
 479};
 480
 481/* IOport from page_base */
 482static const MemoryRegionPortio page_portio_list[] = {
 483    { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, },
 484    { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, },
 485    PORTIO_END_OF_LIST(),
 486};
 487
 488/* IOport from pageh_base */
 489static const MemoryRegionPortio pageh_portio_list[] = {
 490    { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
 491    { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
 492    PORTIO_END_OF_LIST(),
 493};
 494
 495static const MemoryRegionOps cont_io_ops = {
 496    .read = i8257_read_cont,
 497    .write = i8257_write_cont,
 498    .endianness = DEVICE_NATIVE_ENDIAN,
 499    .impl = {
 500        .min_access_size = 1,
 501        .max_access_size = 1,
 502    },
 503};
 504
 505static const VMStateDescription vmstate_i8257_regs = {
 506    .name = "dma_regs",
 507    .version_id = 1,
 508    .minimum_version_id = 1,
 509    .fields = (VMStateField[]) {
 510        VMSTATE_INT32_ARRAY(now, I8257Regs, 2),
 511        VMSTATE_UINT16_ARRAY(base, I8257Regs, 2),
 512        VMSTATE_UINT8(mode, I8257Regs),
 513        VMSTATE_UINT8(page, I8257Regs),
 514        VMSTATE_UINT8(pageh, I8257Regs),
 515        VMSTATE_UINT8(dack, I8257Regs),
 516        VMSTATE_UINT8(eop, I8257Regs),
 517        VMSTATE_END_OF_LIST()
 518    }
 519};
 520
 521static int i8257_post_load(void *opaque, int version_id)
 522{
 523    I8257State *d = opaque;
 524    i8257_dma_run(d);
 525
 526    return 0;
 527}
 528
 529static const VMStateDescription vmstate_i8257 = {
 530    .name = "dma",
 531    .version_id = 1,
 532    .minimum_version_id = 1,
 533    .post_load = i8257_post_load,
 534    .fields = (VMStateField[]) {
 535        VMSTATE_UINT8(command, I8257State),
 536        VMSTATE_UINT8(mask, I8257State),
 537        VMSTATE_UINT8(flip_flop, I8257State),
 538        VMSTATE_INT32(dshift, I8257State),
 539        VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs,
 540                             I8257Regs),
 541        VMSTATE_END_OF_LIST()
 542    }
 543};
 544
 545static void i8257_realize(DeviceState *dev, Error **errp)
 546{
 547    ISADevice *isa = ISA_DEVICE(dev);
 548    I8257State *d = I8257(dev);
 549    int i;
 550
 551    memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d,
 552                          "dma-chan", 8 << d->dshift);
 553    memory_region_add_subregion(isa_address_space_io(isa),
 554                                d->base, &d->channel_io);
 555
 556    isa_register_portio_list(isa, d->page_base, page_portio_list, d,
 557                             "dma-page");
 558    if (d->pageh_base >= 0) {
 559        isa_register_portio_list(isa, d->pageh_base, pageh_portio_list, d,
 560                                 "dma-pageh");
 561    }
 562
 563    memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d,
 564                          "dma-cont", 8 << d->dshift);
 565    memory_region_add_subregion(isa_address_space_io(isa),
 566                                d->base + (8 << d->dshift), &d->cont_io);
 567
 568    for (i = 0; i < ARRAY_SIZE(d->regs); ++i) {
 569        d->regs[i].transfer_handler = i8257_phony_handler;
 570    }
 571
 572    d->dma_bh = qemu_bh_new(i8257_dma_run, d);
 573}
 574
 575static Property i8257_properties[] = {
 576    DEFINE_PROP_INT32("base", I8257State, base, 0x00),
 577    DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80),
 578    DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480),
 579    DEFINE_PROP_INT32("dshift", I8257State, dshift, 0),
 580    DEFINE_PROP_END_OF_LIST()
 581};
 582
 583static void i8257_class_init(ObjectClass *klass, void *data)
 584{
 585    DeviceClass *dc = DEVICE_CLASS(klass);
 586    IsaDmaClass *idc = ISADMA_CLASS(klass);
 587
 588    dc->realize = i8257_realize;
 589    dc->reset = i8257_reset;
 590    dc->vmsd = &vmstate_i8257;
 591    dc->props = i8257_properties;
 592
 593    idc->get_transfer_mode = i8257_dma_get_transfer_mode;
 594    idc->has_autoinitialization = i8257_dma_has_autoinitialization;
 595    idc->read_memory = i8257_dma_read_memory;
 596    idc->write_memory = i8257_dma_write_memory;
 597    idc->hold_DREQ = i8257_dma_hold_DREQ;
 598    idc->release_DREQ = i8257_dma_release_DREQ;
 599    idc->schedule = i8257_dma_schedule;
 600    idc->register_channel = i8257_dma_register_channel;
 601}
 602
 603static const TypeInfo i8257_info = {
 604    .name = TYPE_I8257,
 605    .parent = TYPE_ISA_DEVICE,
 606    .instance_size = sizeof(I8257State),
 607    .class_init = i8257_class_init,
 608    .interfaces = (InterfaceInfo[]) {
 609        { TYPE_ISADMA },
 610        { }
 611    }
 612};
 613
 614static void i8257_register_types(void)
 615{
 616    type_register_static(&i8257_info);
 617}
 618
 619type_init(i8257_register_types)
 620
 621void DMA_init(ISABus *bus, int high_page_enable)
 622{
 623    ISADevice *isa1, *isa2;
 624    DeviceState *d;
 625
 626    isa1 = isa_create(bus, TYPE_I8257);
 627    d = DEVICE(isa1);
 628    qdev_prop_set_int32(d, "base", 0x00);
 629    qdev_prop_set_int32(d, "page-base", 0x80);
 630    qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1);
 631    qdev_prop_set_int32(d, "dshift", 0);
 632    qdev_init_nofail(d);
 633
 634    isa2 = isa_create(bus, TYPE_I8257);
 635    d = DEVICE(isa2);
 636    qdev_prop_set_int32(d, "base", 0xc0);
 637    qdev_prop_set_int32(d, "page-base", 0x88);
 638    qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1);
 639    qdev_prop_set_int32(d, "dshift", 1);
 640    qdev_init_nofail(d);
 641
 642    isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2));
 643}
 644