qemu/hw/misc/stream-fifo.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013 Xilinx Inc
   3 * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
   4 *
   5 * Permission is hereby granted, free of charge, to any person obtaining a copy
   6 * of this software and associated documentation files (the "Software"), to deal
   7 * in the Software without restriction, including without limitation the rights
   8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   9 * copies of the Software, and to permit persons to whom the Software is
  10 * furnished to do so, subject to the following conditions:
  11 *
  12 * The above copyright notice and this permission notice shall be included in
  13 * all copies or substantial portions of the Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21 * THE SOFTWARE.
  22 */
  23
  24#include "qemu/osdep.h"
  25#include "hw/sysbus.h"
  26#include "qemu/log.h"
  27
  28#include "qemu/bitops.h"
  29#include "qapi/qmp/qerror.h"
  30#include "hw/register.h"
  31#include "hw/stream.h"
  32#include "qemu/fifo.h"
  33
  34#ifndef STREAM_FIFO_ERR_DEBUG
  35#define STREAM_FIFO_ERR_DEBUG 0
  36#endif
  37
  38#define TYPE_STREAM_FIFO "stream-fifo"
  39
  40#define STREAM_FIFO(obj) \
  41     OBJECT_CHECK(StreamFifo, (obj), TYPE_STREAM_FIFO)
  42
  43REG32(DP, 0x00)
  44REG32(CTL, 0x04)
  45    #define R_CTL_CORK      (1 << 0)
  46    #define R_CTL_RSVD ~1ull
  47
  48#define R_MAX ((R_CTL) + 1)
  49
  50typedef struct StreamFifo StreamFifo;
  51
  52struct StreamFifo {
  53    SysBusDevice busdev;
  54    MemoryRegion iomem;
  55
  56    Fifo fifo;
  57
  58    uint32_t regs[R_MAX];
  59    RegisterInfo regs_info[R_MAX];
  60
  61    StreamSlave *tx_dev;
  62
  63    StreamCanPushNotifyFn notify;
  64    void *notify_opaque;
  65};
  66
  67static void stream_fifo_notify(void *opaque)
  68{
  69    StreamFifo *s = STREAM_FIFO(opaque);
  70
  71    while (!fifo_is_empty(&s->fifo) && !(s->regs[R_CTL] & R_CTL_CORK) &&
  72           stream_can_push(s->tx_dev, stream_fifo_notify, s)) {
  73        size_t ret;
  74        uint8_t buf[4];
  75
  76        *((uint32_t *)buf) = cpu_to_le32(fifo_pop32(&s->fifo));
  77        ret = stream_push(s->tx_dev, buf, 4, 0);
  78        assert(ret == 4);
  79    }
  80
  81    if (s->notify) {
  82        StreamCanPushNotifyFn notify = s->notify;
  83        s->notify = NULL;
  84        notify(s->notify_opaque);
  85    }
  86}
  87
  88static bool stream_fifo_stream_can_push(StreamSlave *obj,
  89                                        StreamCanPushNotifyFn notify,
  90                                        void *notify_opaque)
  91{
  92    StreamFifo *s = STREAM_FIFO(obj);
  93    bool ret = !(s->regs[R_CTL] & R_CTL_CORK) && !fifo_is_full(&s->fifo);
  94
  95    if (!ret) {
  96        s->notify = notify;
  97        s->notify_opaque = notify_opaque;
  98    }
  99    return ret;
 100}
 101
 102static size_t stream_fifo_stream_push(StreamSlave *obj, uint8_t *buf,
 103                                      size_t len, uint32_t attr)
 104{
 105    StreamFifo *s = STREAM_FIFO(obj);
 106    size_t ret = 0;
 107
 108    assert(!(len % 4));
 109    while (len && !(s->regs[R_CTL] & R_CTL_CORK) && !fifo_is_full(&s->fifo)) {
 110        fifo_push32(&s->fifo, le32_to_cpu(*(uint32_t *)buf));
 111        buf += (sizeof(uint32_t));
 112        len -= 4;
 113        ret += 4;
 114    }
 115    return ret;
 116}
 117
 118
 119static void stream_fifo_update(RegisterInfo *reg, uint64_t val)
 120{
 121    StreamFifo *s = STREAM_FIFO(reg->opaque);
 122
 123    stream_fifo_notify(s);   
 124}
 125
 126static void stream_fifo_dp_post_write(RegisterInfo *reg, uint64_t val)
 127{
 128    StreamFifo *s = STREAM_FIFO(reg->opaque);
 129
 130    if (fifo_is_full(&s->fifo)) {
 131        qemu_log_mask(LOG_GUEST_ERROR, "Write to full fifo\n");
 132    } else {
 133        fifo_push32(&s->fifo, val);
 134    }
 135    stream_fifo_update(reg, val);
 136}
 137
 138static uint64_t stream_fifo_dp_post_read(RegisterInfo *reg, uint64_t val)
 139{
 140    StreamFifo *s = STREAM_FIFO(reg->opaque);
 141
 142    if (fifo_is_empty(&s->fifo)) {
 143        qemu_log_mask(LOG_GUEST_ERROR, "Write to full fifo\n");
 144    } else {
 145        return fifo_pop32(&s->fifo);
 146    }
 147    return 0;
 148}
 149
 150/* TODO: Define register definitions. One entry for each register */
 151
 152static const RegisterAccessInfo stream_fifo_regs_info[] = {
 153    {   .name = "data port",                .decode.addr = A_DP,
 154            .post_write = stream_fifo_dp_post_write,
 155            .post_read = stream_fifo_dp_post_read,
 156    },{ .name = "control",                  .decode.addr = A_CTL,
 157            .rsvd = R_CTL_RSVD,
 158            .reset = R_CTL_CORK,
 159    }
 160};
 161
 162static void stream_fifo_reset(DeviceState *dev)
 163{
 164    StreamFifo *s = STREAM_FIFO(dev);
 165    int i;
 166
 167    for (i = 0; i < R_MAX; ++i) {
 168        register_reset(&s->regs_info[i]);
 169    }
 170
 171    fifo_reset(&s->fifo);
 172}
 173
 174static const MemoryRegionOps stream_fifo_ops = {
 175    .read = register_read_memory_le,
 176    .write = register_write_memory_le,
 177    .endianness = DEVICE_LITTLE_ENDIAN,
 178    .valid = {
 179        .min_access_size = 4,
 180        .max_access_size = 4,
 181    }
 182};
 183
 184static void stream_fifo_realize(DeviceState *dev, Error **errp)
 185{
 186    StreamFifo *s = STREAM_FIFO(dev);
 187    const char *prefix = object_get_canonical_path(OBJECT(dev));
 188    int i;
 189
 190    for (i = 0; i < ARRAY_SIZE(stream_fifo_regs_info); ++i) {
 191        RegisterInfo *r = &s->regs_info[i];
 192
 193        *r = (RegisterInfo) {
 194            .data = (uint8_t *)&s->regs[
 195                    stream_fifo_regs_info[i].decode.addr/4],
 196            .data_size = sizeof(uint32_t),
 197            .access = &stream_fifo_regs_info[i],
 198            .debug = STREAM_FIFO_ERR_DEBUG,
 199            .prefix = prefix,
 200            .opaque = s,
 201        };
 202        memory_region_init_io(&r->mem, OBJECT(dev), &stream_fifo_ops, r,
 203                              r->access->name, 4);
 204        memory_region_add_subregion(&s->iomem, r->access->decode.addr, &r->mem);
 205#define STREAM_FIFO_DEPTH 64
 206        fifo_create32(&s->fifo, STREAM_FIFO_DEPTH);
 207    }
 208}
 209
 210static void stream_fifo_init(Object *obj)
 211{
 212    StreamFifo *s = STREAM_FIFO(obj);
 213
 214    memory_region_init(&s->iomem, obj, "MMIO", R_MAX * 4);
 215    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
 216
 217    object_property_add_link(obj, "stream-connected",
 218                             TYPE_STREAM_SLAVE, (Object **) &s->tx_dev,
 219                             qdev_prop_allow_set_link_before_realize,
 220                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 221                             NULL);
 222
 223}
 224
 225static const VMStateDescription vmstate_stream_fifo = {
 226    .name = "stream_fifo",
 227    .version_id = 1,
 228    .minimum_version_id = 1,
 229    .minimum_version_id_old = 1,
 230    .fields = (VMStateField[]) {
 231        VMSTATE_UINT32_ARRAY(regs, StreamFifo, R_MAX),
 232        VMSTATE_END_OF_LIST(),
 233    }
 234};
 235
 236static void stream_fifo_class_init(ObjectClass *klass, void *data)
 237{
 238    DeviceClass *dc = DEVICE_CLASS(klass);
 239    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 240
 241    dc->reset = stream_fifo_reset;
 242    dc->realize = stream_fifo_realize;
 243    dc->vmsd = &vmstate_stream_fifo;
 244
 245    ssc->push = stream_fifo_stream_push;
 246    ssc->can_push = stream_fifo_stream_can_push;
 247}
 248
 249static const TypeInfo stream_fifo_info = {
 250    .name          = TYPE_STREAM_FIFO,
 251    .parent        = TYPE_SYS_BUS_DEVICE,
 252    .instance_size = sizeof(StreamFifo),
 253    .class_init    = stream_fifo_class_init,
 254    .instance_init = stream_fifo_init,
 255    .interfaces = (InterfaceInfo[]) {
 256        { TYPE_STREAM_SLAVE },
 257        { },
 258    }
 259};
 260
 261static void stream_fifo_register_types(void)
 262{
 263    type_register_static(&stream_fifo_info);
 264}
 265
 266type_init(stream_fifo_register_types)
 267