diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/block/m25p80.c | 52 | ||||
-rw-r--r-- | hw/core/stream.c | 15 | ||||
-rw-r--r-- | hw/dma/xilinx_axidma.c | 261 | ||||
-rw-r--r-- | hw/microblaze/petalogix_ml605_mmu.c | 28 | ||||
-rw-r--r-- | hw/net/xilinx_axienet.c | 255 |
5 files changed, 468 insertions, 143 deletions
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 55e9d0d37a..efcc7f4c83 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -26,15 +26,17 @@ #include "hw/ssi.h" #include "hw/devices.h" -#ifdef M25P80_ERR_DEBUG -#define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } while (0); -#else - #define DB_PRINT(...) +#ifndef M25P80_ERR_DEBUG +#define M25P80_ERR_DEBUG 0 #endif +#define DB_PRINT_L(level, ...) do { \ + if (M25P80_ERR_DEBUG > (level)) { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } \ +} while (0); + /* Fields for FlashPartInfo->flags */ /* erase capabilities */ @@ -317,13 +319,14 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) abort(); } - DB_PRINT("offset = %#x, len = %d\n", offset, len); + DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); if ((s->pi->flags & capa_to_assert) != capa_to_assert) { - hw_error("m25p80: %dk erase size not supported by device\n", len); + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" + " device\n", len); } if (!s->write_enable) { - DB_PRINT("erase with write protect!\n"); + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n"); return; } memset(s->storage + offset, 0xff, len); @@ -345,12 +348,12 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) uint8_t prev = s->storage[s->cur_addr]; if (!s->write_enable) { - DB_PRINT("write with write protect!\n"); + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); } if ((prev ^ data) & data) { - DB_PRINT("programming zero to one! addr=%lx %x -> %x\n", - addr, prev, data); + DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 + " -> %" PRIx8 "\n", addr, prev, data); } if (s->pi->flags & WR_1) { @@ -403,7 +406,7 @@ static void complete_collecting_data(Flash *s) static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; - DB_PRINT("decoded new command:%x\n", value); + DB_PRINT_L(0, "decoded new command:%x\n", value); switch (value) { @@ -483,7 +486,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case JEDEC_READ: - DB_PRINT("populated jedec code\n"); + DB_PRINT_L(0, "populated jedec code\n"); s->data[0] = (s->pi->jedec >> 16) & 0xff; s->data[1] = (s->pi->jedec >> 8) & 0xff; s->data[2] = s->pi->jedec & 0xff; @@ -500,16 +503,17 @@ static void decode_new_cmd(Flash *s, uint32_t value) case BULK_ERASE: if (s->write_enable) { - DB_PRINT("chip erase\n"); + DB_PRINT_L(0, "chip erase\n"); flash_erase(s, 0, BULK_ERASE); } else { - DB_PRINT("chip erase with write protect!\n"); + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " + "protect!\n"); } break; case NOP: break; default: - DB_PRINT("Unknown cmd %x\n", value); + qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; } } @@ -525,7 +529,7 @@ static int m25p80_cs(SSISlave *ss, bool select) flash_sync_dirty(s, -1); } - DB_PRINT("%sselect\n", select ? "de" : ""); + DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); return 0; } @@ -538,15 +542,16 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr, - (uint8_t)tx); + DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n", + s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr++; break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r); + DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr, + (uint8_t)r); s->cur_addr = (s->cur_addr + 1) % s->size; break; @@ -592,7 +597,7 @@ static int m25p80_init(SSISlave *ss) dinfo = drive_get_next(IF_MTD); if (dinfo && dinfo->bdrv) { - DB_PRINT("Binding to IF_MTD drive\n"); + DB_PRINT_L(0, "Binding to IF_MTD drive\n"); s->bdrv = dinfo->bdrv; /* FIXME: Move to late init */ if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size, @@ -601,6 +606,7 @@ static int m25p80_init(SSISlave *ss) return 1; } } else { + DB_PRINT_L(0, "No BDRV - binding to RAM\n"); memset(s->storage, 0xFF, s->size); } diff --git a/hw/core/stream.c b/hw/core/stream.c index a07d6a56d3..e6a05a543e 100644 --- a/hw/core/stream.c +++ b/hw/core/stream.c @@ -1,11 +1,20 @@ #include "hw/stream.h" -void -stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app) +size_t +stream_push(StreamSlave *sink, uint8_t *buf, size_t len) { StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); - k->push(sink, buf, len, app); + return k->push(sink, buf, len); +} + +bool +stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify, + void *notify_opaque) +{ + StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); + + return k->can_push ? k->can_push(sink, notify, notify_opaque) : true; } static const TypeInfo stream_slave_info = { diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 8db1a74acf..1c23762210 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -27,17 +27,39 @@ #include "hw/ptimer.h" #include "qemu/log.h" #include "hw/qdev-addr.h" +#include "qapi/qmp/qerror.h" #include "hw/stream.h" #define D(x) +#define TYPE_XILINX_AXI_DMA "xlnx.axi-dma" +#define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream" +#define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream" + +#define XILINX_AXI_DMA(obj) \ + OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA) + +#define XILINX_AXI_DMA_DATA_STREAM(obj) \ + OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ + TYPE_XILINX_AXI_DMA_DATA_STREAM) + +#define XILINX_AXI_DMA_CONTROL_STREAM(obj) \ + OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ + TYPE_XILINX_AXI_DMA_CONTROL_STREAM) + #define R_DMACR (0x00 / 4) #define R_DMASR (0x04 / 4) #define R_CURDESC (0x08 / 4) #define R_TAILDESC (0x10 / 4) #define R_MAX (0x30 / 4) +#define CONTROL_PAYLOAD_WORDS 5 +#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) + +typedef struct XilinxAXIDMA XilinxAXIDMA; +typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave; + enum { DMACR_RUNSTOP = 1, DMACR_TAILPTR_MODE = 2, @@ -59,7 +81,7 @@ struct SDesc { uint64_t reserved; uint32_t control; uint32_t status; - uint32_t app[6]; + uint8_t app[CONTROL_PAYLOAD_SIZE]; }; enum { @@ -87,15 +109,28 @@ struct Stream { int pos; unsigned int complete_cnt; uint32_t regs[R_MAX]; + uint8_t app[20]; +}; + +struct XilinxAXIDMAStreamSlave { + Object parent; + + struct XilinxAXIDMA *dma; }; struct XilinxAXIDMA { SysBusDevice busdev; MemoryRegion iomem; uint32_t freqhz; - StreamSlave *tx_dev; + StreamSlave *tx_data_dev; + StreamSlave *tx_control_dev; + XilinxAXIDMAStreamSlave rx_data_dev; + XilinxAXIDMAStreamSlave rx_control_dev; struct Stream streams[2]; + + StreamCanPushNotifyFn notify; + void *notify_opaque; }; /* @@ -161,7 +196,6 @@ static void stream_desc_show(struct SDesc *d) static void stream_desc_load(struct Stream *s, hwaddr addr) { struct SDesc *d = &s->desc; - int i; cpu_physical_memory_read(addr, (void *) d, sizeof *d); @@ -170,24 +204,17 @@ static void stream_desc_load(struct Stream *s, hwaddr addr) d->nxtdesc = le64_to_cpu(d->nxtdesc); d->control = le32_to_cpu(d->control); d->status = le32_to_cpu(d->status); - for (i = 0; i < ARRAY_SIZE(d->app); i++) { - d->app[i] = le32_to_cpu(d->app[i]); - } } static void stream_desc_store(struct Stream *s, hwaddr addr) { struct SDesc *d = &s->desc; - int i; /* Convert from host endianness into LE. */ d->buffer_address = cpu_to_le64(d->buffer_address); d->nxtdesc = cpu_to_le64(d->nxtdesc); d->control = cpu_to_le32(d->control); d->status = cpu_to_le32(d->status); - for (i = 0; i < ARRAY_SIZE(d->app); i++) { - d->app[i] = cpu_to_le32(d->app[i]); - } cpu_physical_memory_write(addr, (void *) d, sizeof *d); } @@ -239,13 +266,12 @@ static void stream_complete(struct Stream *s) } } -static void stream_process_mem2s(struct Stream *s, - StreamSlave *tx_dev) +static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev, + StreamSlave *tx_control_dev) { uint32_t prev_d; unsigned char txbuf[16 * 1024]; unsigned int txlen; - uint32_t app[6]; if (!stream_running(s) || stream_idle(s)) { return; @@ -255,13 +281,13 @@ static void stream_process_mem2s(struct Stream *s, stream_desc_load(s, s->regs[R_CURDESC]); if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_IDLE; + s->regs[R_DMASR] |= DMASR_HALTED; break; } if (stream_desc_sof(&s->desc)) { s->pos = 0; - memcpy(app, s->desc.app, sizeof app); + stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app)); } txlen = s->desc.control & SDESC_CTRL_LEN_MASK; @@ -275,7 +301,7 @@ static void stream_process_mem2s(struct Stream *s, s->pos += txlen; if (stream_desc_eof(&s->desc)) { - stream_push(tx_dev, txbuf, s->pos, app); + stream_push(tx_data_dev, txbuf, s->pos); s->pos = 0; stream_complete(s); } @@ -294,23 +320,23 @@ static void stream_process_mem2s(struct Stream *s, } } -static void stream_process_s2mem(struct Stream *s, - unsigned char *buf, size_t len, uint32_t *app) +static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, + size_t len) { uint32_t prev_d; unsigned int rxlen; - int pos = 0; + size_t pos = 0; int sof = 1; if (!stream_running(s) || stream_idle(s)) { - return; + return 0; } while (len) { stream_desc_load(s, s->regs[R_CURDESC]); if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_IDLE; + s->regs[R_DMASR] |= DMASR_HALTED; break; } @@ -326,12 +352,8 @@ static void stream_process_s2mem(struct Stream *s, /* Update the descriptor. */ if (!len) { - int i; - stream_complete(s); - for (i = 0; i < 5; i++) { - s->desc.app[i] = app[i]; - } + memcpy(s->desc.app, s->app, sizeof(s->desc.app)); s->desc.status |= SDESC_STATUS_EOF; } @@ -348,25 +370,69 @@ static void stream_process_s2mem(struct Stream *s, break; } } + + return pos; +} + +static void xilinx_axidma_reset(DeviceState *dev) +{ + int i; + XilinxAXIDMA *s = XILINX_AXI_DMA(dev); + + for (i = 0; i < 2; i++) { + stream_reset(&s->streams[i]); + } } -static void -axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app) +static size_t +xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf, + size_t len) { - struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj)); - struct Stream *s = &d->streams[1]; + XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj); + struct Stream *s = &cs->dma->streams[1]; - if (!app) { - hw_error("No stream app data!\n"); + if (len != CONTROL_PAYLOAD_SIZE) { + hw_error("AXI DMA requires %d byte control stream payload\n", + (int)CONTROL_PAYLOAD_SIZE); } - stream_process_s2mem(s, buf, len, app); + + memcpy(s->app, buf, len); + return len; +} + +static bool +xilinx_axidma_data_stream_can_push(StreamSlave *obj, + StreamCanPushNotifyFn notify, + void *notify_opaque) +{ + XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); + struct Stream *s = &ds->dma->streams[1]; + + if (!stream_running(s) || stream_idle(s)) { + ds->dma->notify = notify; + ds->dma->notify_opaque = notify_opaque; + return false; + } + + return true; +} + +static size_t +xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len) +{ + XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); + struct Stream *s = &ds->dma->streams[1]; + size_t ret; + + ret = stream_process_s2mem(s, buf, len); stream_update_irq(s); + return ret; } static uint64_t axidma_read(void *opaque, hwaddr addr, unsigned size) { - struct XilinxAXIDMA *d = opaque; + XilinxAXIDMA *d = opaque; struct Stream *s; uint32_t r = 0; int sid; @@ -401,7 +467,7 @@ static uint64_t axidma_read(void *opaque, hwaddr addr, static void axidma_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct XilinxAXIDMA *d = opaque; + XilinxAXIDMA *d = opaque; struct Stream *s; int sid; @@ -439,7 +505,7 @@ static void axidma_write(void *opaque, hwaddr addr, s->regs[addr] = value; s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ if (!sid) { - stream_process_mem2s(s, d->tx_dev); + stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev); } break; default: @@ -448,6 +514,10 @@ static void axidma_write(void *opaque, hwaddr addr, s->regs[addr] = value; break; } + if (sid == 1 && d->notify) { + d->notify(d->notify_opaque); + d->notify = NULL; + } stream_update_irq(s); } @@ -457,58 +527,131 @@ static const MemoryRegionOps axidma_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int xilinx_axidma_init(SysBusDevice *dev) +static void xilinx_axidma_realize(DeviceState *dev, Error **errp) { - struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev); - int i; - - sysbus_init_irq(dev, &s->streams[0].irq); - sysbus_init_irq(dev, &s->streams[1].irq); + XilinxAXIDMA *s = XILINX_AXI_DMA(dev); + XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev); + XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM( + &s->rx_control_dev); + Error *local_errp = NULL; + + object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, + (Object **)&ds->dma, &local_errp); + object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, + (Object **)&cs->dma, &local_errp); + if (local_errp) { + goto xilinx_axidma_realize_fail; + } + object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_errp); + object_property_set_link(OBJECT(cs), OBJECT(s), "dma", &local_errp); + if (local_errp) { + goto xilinx_axidma_realize_fail; + } - memory_region_init_io(&s->iomem, &axidma_ops, s, - "xlnx.axi-dma", R_MAX * 4 * 2); - sysbus_init_mmio(dev, &s->iomem); + int i; for (i = 0; i < 2; i++) { - stream_reset(&s->streams[i]); s->streams[i].nr = i; s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]); s->streams[i].ptimer = ptimer_init(s->streams[i].bh); ptimer_set_freq(s->streams[i].ptimer, s->freqhz); } - return 0; + return; + +xilinx_axidma_realize_fail: + if (!*errp) { + *errp = local_errp; + } } -static void xilinx_axidma_initfn(Object *obj) +static void xilinx_axidma_init(Object *obj) { - struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + XilinxAXIDMA *s = XILINX_AXI_DMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + Error *errp = NULL; object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **) &s->tx_dev, NULL); + (Object **) &s->tx_data_dev, &errp); + assert_no_error(errp); + object_property_add_link(obj, "axistream-control-connected", + TYPE_STREAM_SLAVE, + (Object **) &s->tx_control_dev, &errp); + assert_no_error(errp); + + object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_DMA_DATA_STREAM); + object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_DMA_CONTROL_STREAM); + object_property_add_child(OBJECT(s), "axistream-connected-target", + (Object *)&s->rx_data_dev, &errp); + assert_no_error(errp); + object_property_add_child(OBJECT(s), "axistream-control-connected-target", + (Object *)&s->rx_control_dev, &errp); + assert_no_error(errp); + + sysbus_init_irq(sbd, &s->streams[0].irq); + sysbus_init_irq(sbd, &s->streams[1].irq); + + memory_region_init_io(&s->iomem, &axidma_ops, s, + "xlnx.axi-dma", R_MAX * 4 * 2); + sysbus_init_mmio(sbd, &s->iomem); } static Property axidma_properties[] = { - DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), + DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000), DEFINE_PROP_END_OF_LIST(), }; static void axidma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); - k->init = xilinx_axidma_init; + dc->realize = xilinx_axidma_realize, + dc->reset = xilinx_axidma_reset; dc->props = axidma_properties; - ssc->push = axidma_push; +} + +static StreamSlaveClass xilinx_axidma_data_stream_class = { + .push = xilinx_axidma_data_stream_push, + .can_push = xilinx_axidma_data_stream_can_push, +}; + +static StreamSlaveClass xilinx_axidma_control_stream_class = { + .push = xilinx_axidma_control_stream_push, +}; + +static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) +{ + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); + + ssc->push = ((StreamSlaveClass *)data)->push; + ssc->can_push = ((StreamSlaveClass *)data)->can_push; } static const TypeInfo axidma_info = { - .name = "xlnx.axi-dma", + .name = TYPE_XILINX_AXI_DMA, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct XilinxAXIDMA), + .instance_size = sizeof(XilinxAXIDMA), .class_init = axidma_class_init, - .instance_init = xilinx_axidma_initfn, + .instance_init = xilinx_axidma_init, +}; + +static const TypeInfo xilinx_axidma_data_stream_info = { + .name = TYPE_XILINX_AXI_DMA_DATA_STREAM, + .parent = TYPE_OBJECT, + .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), + .class_init = xilinx_axidma_stream_class_init, + .class_data = &xilinx_axidma_data_stream_class, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } +}; + +static const TypeInfo xilinx_axidma_control_stream_info = { + .name = TYPE_XILINX_AXI_DMA_CONTROL_STREAM, + .parent = TYPE_OBJECT, + .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), + .class_init = xilinx_axidma_stream_class_init, + .class_data = &xilinx_axidma_control_stream_class, .interfaces = (InterfaceInfo[]) { { TYPE_STREAM_SLAVE }, { } @@ -518,6 +661,8 @@ static const TypeInfo axidma_info = { static void xilinx_axidma_register_types(void) { type_register_static(&axidma_info); + type_register_static(&xilinx_axidma_data_stream_info); + type_register_static(&xilinx_axidma_control_stream_info); } type_init(xilinx_axidma_register_types) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index ae7ff44423..334046808f 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -79,6 +79,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) const char *cpu_model = args->cpu_model; MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev, *dma, *eth0; + Object *ds, *cs; MicroBlazeCPU *cpu; SysBusDevice *busdev; CPUMBState *env; @@ -134,14 +135,25 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) dma = qdev_create(NULL, "xlnx.axi-dma"); /* FIXME: attach to the sysbus instead */ - object_property_add_child(container_get(qdev_get_machine(), "/unattached"), - "xilinx-dma", OBJECT(dma), NULL); - - xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(dma), - 0x82780000, irq[3], 0x1000, 0x1000); - - xilinx_axidma_init(dma, STREAM_SLAVE(eth0), 0x84600000, irq[1], irq[0], - 100 * 1000000); + object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0), + NULL); + object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma), + NULL); + + ds = object_property_get_link(OBJECT(dma), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(dma), + "axistream-control-connected-target", NULL); + xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(ds), + STREAM_SLAVE(cs), 0x82780000, irq[3], 0x1000, + 0x1000); + + ds = object_property_get_link(OBJECT(eth0), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(eth0), + "axistream-control-connected-target", NULL); + xilinx_axidma_init(dma, STREAM_SLAVE(ds), STREAM_SLAVE(cs), 0x84600000, + irq[1], irq[0], 100 * 1000000); { SSIBus *spi; diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 07c4badd98..8989e95297 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -32,12 +32,30 @@ #define DPHY(x) +#define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet" +#define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream" +#define TYPE_XILINX_AXI_ENET_CONTROL_STREAM "xilinx-axienet-control-stream" + +#define XILINX_AXI_ENET(obj) \ + OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET) + +#define XILINX_AXI_ENET_DATA_STREAM(obj) \ + OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\ + TYPE_XILINX_AXI_ENET_DATA_STREAM) + +#define XILINX_AXI_ENET_CONTROL_STREAM(obj) \ + OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\ + TYPE_XILINX_AXI_ENET_CONTROL_STREAM) + /* Advertisement control register. */ #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ #define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ #define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define CONTROL_PAYLOAD_WORDS 5 +#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) + struct PHY { uint32_t regs[32]; @@ -306,11 +324,23 @@ struct TEMAC { void *parent; }; +typedef struct XilinxAXIEnetStreamSlave XilinxAXIEnetStreamSlave; +typedef struct XilinxAXIEnet XilinxAXIEnet; + +struct XilinxAXIEnetStreamSlave { + Object parent; + + struct XilinxAXIEnet *enet; +} ; + struct XilinxAXIEnet { SysBusDevice busdev; MemoryRegion iomem; qemu_irq irq; - StreamSlave *tx_dev; + StreamSlave *tx_data_dev; + StreamSlave *tx_control_dev; + XilinxAXIEnetStreamSlave rx_data_dev; + XilinxAXIEnetStreamSlave rx_control_dev; NICState *nic; NICConf conf; @@ -361,42 +391,50 @@ struct XilinxAXIEnet { /* 32K x 1 lookup filter. */ uint32_t ext_mtable[1024]; + uint32_t hdr[CONTROL_PAYLOAD_WORDS]; uint8_t *rxmem; + uint32_t rxsize; + uint32_t rxpos; + + uint8_t rxapp[CONTROL_PAYLOAD_SIZE]; + uint32_t rxappsize; }; -static void axienet_rx_reset(struct XilinxAXIEnet *s) +static void axienet_rx_reset(XilinxAXIEnet *s) { s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN; } -static void axienet_tx_reset(struct XilinxAXIEnet *s) +static void axienet_tx_reset(XilinxAXIEnet *s) { s->tc = TC_JUM | TC_TX | TC_VLAN; } -static inline int axienet_rx_resetting(struct XilinxAXIEnet *s) +static inline int axienet_rx_resetting(XilinxAXIEnet *s) { return s->rcw[1] & RCW1_RST; } -static inline int axienet_rx_enabled(struct XilinxAXIEnet *s) +static inline int axienet_rx_enabled(XilinxAXIEnet *s) { return s->rcw[1] & RCW1_RX; } -static inline int axienet_extmcf_enabled(struct XilinxAXIEnet *s) +static inline int axienet_extmcf_enabled(XilinxAXIEnet *s) { return !!(s->regs[R_RAF] & RAF_EMCF_EN); } -static inline int axienet_newfunc_enabled(struct XilinxAXIEnet *s) +static inline int axienet_newfunc_enabled(XilinxAXIEnet *s) { return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN); } -static void axienet_reset(struct XilinxAXIEnet *s) +static void xilinx_axienet_reset(DeviceState *d) { + XilinxAXIEnet *s = XILINX_AXI_ENET(d); + axienet_rx_reset(s); axienet_tx_reset(s); @@ -406,7 +444,7 @@ static void axienet_reset(struct XilinxAXIEnet *s) s->emmc = EMMC_LINKSPEED_100MB; } -static void enet_update_irq(struct XilinxAXIEnet *s) +static void enet_update_irq(XilinxAXIEnet *s) { s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE]; qemu_set_irq(s->irq, !!s->regs[R_IP]); @@ -414,7 +452,7 @@ static void enet_update_irq(struct XilinxAXIEnet *s) static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) { - struct XilinxAXIEnet *s = opaque; + XilinxAXIEnet *s = opaque; uint32_t r = 0; addr >>= 2; @@ -506,7 +544,7 @@ static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) static void enet_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct XilinxAXIEnet *s = opaque; + XilinxAXIEnet *s = opaque; struct TEMAC *t = &s->TEMAC; addr >>= 2; @@ -620,10 +658,10 @@ static const MemoryRegionOps enet_ops = { static int eth_can_rx(NetClientState *nc) { - struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); + XilinxAXIEnet *s = qemu_get_nic_opaque(nc); /* RX enabled? */ - return !axienet_rx_resetting(s) && axienet_rx_enabled(s); + return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s); } static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) @@ -641,13 +679,38 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) return match; } +static void axienet_eth_rx_notify(void *opaque) +{ + XilinxAXIEnet *s = XILINX_AXI_ENET(opaque); + + while (s->rxappsize && stream_can_push(s->tx_control_dev, + axienet_eth_rx_notify, s)) { + size_t ret = stream_push(s->tx_control_dev, + (void *)s->rxapp + CONTROL_PAYLOAD_SIZE + - s->rxappsize, s->rxappsize); + s->rxappsize -= ret; + } + + while (s->rxsize && stream_can_push(s->tx_data_dev, + axienet_eth_rx_notify, s)) { + size_t ret = stream_push(s->tx_data_dev, (void *)s->rxmem + s->rxpos, + s->rxsize); + s->rxsize -= ret; + s->rxpos += ret; + if (!s->rxsize) { + s->regs[R_IS] |= IS_RX_COMPLETE; + } + } + enet_update_irq(s); +} + static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); + XilinxAXIEnet *s = qemu_get_nic_opaque(nc); static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52}; - uint32_t app[6] = {0}; + uint32_t app[CONTROL_PAYLOAD_WORDS] = {0}; int promisc = s->fmi & (1 << 31); int unicast, broadcast, multicast, ip_multicast = 0; uint32_t csum32; @@ -778,9 +841,15 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) /* Good frame. */ app[2] |= 1 << 6; - stream_push(s->tx_dev, (void *)s->rxmem, size, app); + s->rxsize = size; + s->rxpos = 0; + for (i = 0; i < ARRAY_SIZE(app); ++i) { + app[i] = cpu_to_le32(app[i]); + } + s->rxappsize = CONTROL_PAYLOAD_SIZE; + memcpy(s->rxapp, app, s->rxappsize); + axienet_eth_rx_notify(s); - s->regs[R_IS] |= IS_RX_COMPLETE; enet_update_irq(s); return size; } @@ -788,38 +857,59 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void eth_cleanup(NetClientState *nc) { /* FIXME. */ - struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); + XilinxAXIEnet *s = qemu_get_nic_opaque(nc); g_free(s->rxmem); g_free(s); } -static void -axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) +static size_t +xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len) { - struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + int i; + XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(obj); + XilinxAXIEnet *s = cs->enet; + + if (len != CONTROL_PAYLOAD_SIZE) { + hw_error("AXI Enet requires %d byte control stream payload\n", + (int)CONTROL_PAYLOAD_SIZE); + } + + memcpy(s->hdr, buf, len); + + for (i = 0; i < ARRAY_SIZE(s->hdr); ++i) { + s->hdr[i] = le32_to_cpu(s->hdr[i]); + } + return len; +} + +static size_t +xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size) +{ + XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj); + XilinxAXIEnet *s = ds->enet; /* TX enable ? */ if (!(s->tc & TC_TX)) { - return; + return size; } /* Jumbo or vlan sizes ? */ if (!(s->tc & TC_JUM)) { if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) { - return; + return size; } } - if (hdr[0] & 1) { - unsigned int start_off = hdr[1] >> 16; - unsigned int write_off = hdr[1] & 0xffff; + if (s->hdr[0] & 1) { + unsigned int start_off = s->hdr[1] >> 16; + unsigned int write_off = s->hdr[1] & 0xffff; uint32_t tmp_csum; uint16_t csum; tmp_csum = net_checksum_add(size - start_off, (uint8_t *)buf + start_off); /* Accumulate the seed. */ - tmp_csum += hdr[2] & 0xffff; + tmp_csum += s->hdr[2] & 0xffff; /* Fold the 32bit partial checksum. */ csum = net_checksum_finish(tmp_csum); @@ -834,6 +924,8 @@ axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) s->stats.tx_bytes += size; s->regs[R_IS] |= IS_TX_COMPLETE; enet_update_irq(s); + + return size; } static NetClientInfo net_xilinx_enet_info = { @@ -844,18 +936,30 @@ static NetClientInfo net_xilinx_enet_info = { .cleanup = eth_cleanup, }; -static int xilinx_enet_init(SysBusDevice *dev) +static void xilinx_enet_realize(DeviceState *dev, Error **errp) { - struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000); - sysbus_init_mmio(dev, &s->iomem); + XilinxAXIEnet *s = XILINX_AXI_ENET(dev); + XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev); + XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM( + &s->rx_control_dev); + Error *local_errp = NULL; + + object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet", + (Object **) &ds->enet, &local_errp); + object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet", + (Object **) &cs->enet, &local_errp); + if (local_errp) { + goto xilinx_enet_realize_fail; + } + object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_errp); + object_property_set_link(OBJECT(cs), OBJECT(s), "enet", &local_errp); + if (local_errp) { + goto xilinx_enet_realize_fail; + } qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->qdev.id, s); + object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); tdk_init(&s->TEMAC.phy); @@ -864,46 +968,93 @@ static int xilinx_enet_init(SysBusDevice *dev) s->TEMAC.parent = s; s->rxmem = g_malloc(s->c_rxmem); - axienet_reset(s); + return; - return 0; +xilinx_enet_realize_fail: + if (!*errp) { + *errp = local_errp; + } } -static void xilinx_enet_initfn(Object *obj) +static void xilinx_enet_init(Object *obj) { - struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + XilinxAXIEnet *s = XILINX_AXI_ENET(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); Error *errp = NULL; object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **) &s->tx_dev, &errp); + (Object **) &s->tx_data_dev, &errp); assert_no_error(errp); + object_property_add_link(obj, "axistream-control-connected", + TYPE_STREAM_SLAVE, + (Object **) &s->tx_control_dev, &errp); + assert_no_error(errp); + + object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_ENET_DATA_STREAM); + object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_ENET_CONTROL_STREAM); + object_property_add_child(OBJECT(s), "axistream-connected-target", + (Object *)&s->rx_data_dev, &errp); + assert_no_error(errp); + object_property_add_child(OBJECT(s), "axistream-control-connected-target", + (Object *)&s->rx_control_dev, &errp); + assert_no_error(errp); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000); + sysbus_init_mmio(sbd, &s->iomem); } static Property xilinx_enet_properties[] = { - DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7), - DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000), - DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000), - DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf), + DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7), + DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000), + DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000), + DEFINE_NIC_PROPERTIES(XilinxAXIEnet, conf), DEFINE_PROP_END_OF_LIST(), }; static void xilinx_enet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); - k->init = xilinx_enet_init; + dc->realize = xilinx_enet_realize; dc->props = xilinx_enet_properties; - ssc->push = axienet_stream_push; + dc->reset = xilinx_axienet_reset; +} + +static void xilinx_enet_stream_class_init(ObjectClass *klass, void *data) +{ + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); + + ssc->push = data; } static const TypeInfo xilinx_enet_info = { - .name = "xlnx.axi-ethernet", + .name = TYPE_XILINX_AXI_ENET, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct XilinxAXIEnet), + .instance_size = sizeof(XilinxAXIEnet), .class_init = xilinx_enet_class_init, - .instance_init = xilinx_enet_initfn, + .instance_init = xilinx_enet_init, +}; + +static const TypeInfo xilinx_enet_data_stream_info = { + .name = TYPE_XILINX_AXI_ENET_DATA_STREAM, + .parent = TYPE_OBJECT, + .instance_size = sizeof(struct XilinxAXIEnetStreamSlave), + .class_init = xilinx_enet_stream_class_init, + .class_data = xilinx_axienet_data_stream_push, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } +}; + +static const TypeInfo xilinx_enet_control_stream_info = { + .name = TYPE_XILINX_AXI_ENET_CONTROL_STREAM, + .parent = TYPE_OBJECT, + .instance_size = sizeof(struct XilinxAXIEnetStreamSlave), + .class_init = xilinx_enet_stream_class_init, + .class_data = xilinx_axienet_control_stream_push, .interfaces = (InterfaceInfo[]) { { TYPE_STREAM_SLAVE }, { } @@ -913,6 +1064,8 @@ static const TypeInfo xilinx_enet_info = { static void xilinx_enet_register_types(void) { type_register_static(&xilinx_enet_info); + type_register_static(&xilinx_enet_data_stream_info); + type_register_static(&xilinx_enet_control_stream_info); } type_init(xilinx_enet_register_types) |