From: crwulff@gmail.com
To: qemu-devel@nongnu.org
Cc: Chris Wulff <crwulff@gmail.com>
Subject: [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices.
Date: Sun, 9 Sep 2012 20:20:02 -0400 [thread overview]
Message-ID: <1347236407-10465-5-git-send-email-crwulff@gmail.com> (raw)
In-Reply-To: <1347236407-10465-1-git-send-email-crwulff@gmail.com>
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
hw/Makefile.objs | 7 +
hw/labx_audio_depacketizer.c | 409 ++++++++++++++++++++++++++++
hw/labx_audio_packetizer.c | 397 +++++++++++++++++++++++++++
hw/labx_devices.h | 103 +++++++
hw/labx_dma.c | 241 +++++++++++++++++
hw/labx_ethernet.c | 615 ++++++++++++++++++++++++++++++++++++++++++
hw/labx_ptp.c | 291 ++++++++++++++++++++
7 files changed, 2063 insertions(+)
create mode 100644 hw/labx_audio_depacketizer.c
create mode 100644 hw/labx_audio_packetizer.c
create mode 100644 hw/labx_devices.h
create mode 100644 hw/labx_dma.c
create mode 100644 hw/labx_ethernet.c
create mode 100644 hw/labx_ptp.c
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 59dd2d5..ebbeb16 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -72,6 +72,13 @@ hw-obj-$(CONFIG_ALTERA) += altera_vic.o
hw-obj-$(CONFIG_ALTERA) += altera_uart.o
hw-obj-$(CONFIG_ALTERA) += altera_timer.o
+# Lab X devices
+hw-obj-$(CONFIG_LABX) += labx_audio_packetizer.o
+hw-obj-$(CONFIG_LABX) += labx_audio_depacketizer.o
+hw-obj-$(CONFIG_LABX) += labx_dma.o
+hw-obj-$(CONFIG_LABX) += labx_ethernet.o
+hw-obj-$(CONFIG_LABX) += labx_ptp.o
+
# PKUnity SoC devices
hw-obj-$(CONFIG_PUV3) += puv3_intc.o
hw-obj-$(CONFIG_PUV3) += puv3_ost.o
diff --git a/hw/labx_audio_depacketizer.c b/hw/labx_audio_depacketizer.c
new file mode 100644
index 0000000..5da3f47
--- /dev/null
+++ b/hw/labx_audio_depacketizer.c
@@ -0,0 +1,409 @@
+
+/*
+ * QEMU model of the LabX audio depacketizer.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "labx_devices.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+struct clock_domain_info {
+ uint32_t tsInterval;
+};
+
+typedef struct audio_depacketizer {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_depacketizer;
+ MemoryRegion mmio_clock_domain;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t clockDomains;
+ uint32_t cacheDataWords;
+ uint32_t paramWords;
+ uint32_t microcodeWords;
+ uint32_t maxStreamSlots;
+ uint32_t maxStreams;
+ uint32_t hasDMA;
+ uint32_t matchArch;
+
+ /* IRQ */
+ qemu_irq irq;
+
+ /* Values set by drivers */
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+
+ /* Clock domain information */
+ struct clock_domain_info *clockDomainInfo;
+
+ /* Attached DMA (if hasDMA > 0) */
+ DeviceState *dma;
+} depacketizer_t;
+
+/*
+ * Depacketizer registers
+ */
+static uint64_t depacketizer_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* vector bar */
+ break;
+
+ case 0x02: /* id select 0 */
+ break;
+
+ case 0x03: /* id select 1 */
+ break;
+
+ case 0x04: /* id select 2 */
+ break;
+
+ case 0x05: /* id select 3 */
+ break;
+
+ case 0x06: /* id config data */
+ break;
+
+ case 0x08: /* irq mask */
+ break;
+
+ case 0x09: /* irq flags */
+ break;
+
+ case 0x0A: /* sync */
+ break;
+
+ case 0x0B: /* relocate */
+ break;
+
+ case 0x0C: /* stream status 0 */
+ break;
+
+ case 0x0D: /* stream status 1 */
+ break;
+
+ case 0x0E: /* stream status 2 */
+ break;
+
+ case 0x0F: /* stream status 3 */
+ break;
+
+ case 0xFD: /* capabilities a */
+ retval = (p->maxStreamSlots & 0x7F);
+ break;
+
+ case 0xFE: /* capabilities b */
+ retval = ((p->matchArch & 0xFF) << 24) |
+ ((p->maxStreams & 0xFF) << 16) |
+ ((p->clockDomains & 0xFF) << 8) |
+ ((min_bits(p->paramWords-1) & 0x0F) << 4) |
+ ((min_bits(p->microcodeWords-1) & 0x0F));
+ break;
+
+ case 0xFF: /* revision */
+ retval = 0x00000014;
+ break;
+
+ default:
+ printf("labx-audio-depacketizer: Read of unknown register %08X\n",
+ addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void depacketizer_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*depacketizer_t *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* vector bar */
+ break;
+
+ case 0x02: /* id select 0 */
+ break;
+
+ case 0x03: /* id select 1 */
+ break;
+
+ case 0x04: /* id select 2 */
+ break;
+
+ case 0x05: /* id select 3 */
+ break;
+
+ case 0x06: /* id config data */
+ break;
+
+ case 0x08: /* irq mask */
+ break;
+
+ case 0x09: /* irq flags */
+ break;
+
+ case 0x0A: /* sync */
+ break;
+
+ case 0x0B: /* relocate */
+ break;
+
+ case 0x0C: /* stream status 0 */
+ break;
+
+ case 0x0D: /* stream status 1 */
+ break;
+
+ case 0x0E: /* stream status 2 */
+ break;
+
+ case 0x0F: /* stream status 3 */
+ break;
+
+ case 0xFD: /* capabilities a */
+ break;
+
+ case 0xFE: /* capabilities b */
+ break;
+
+ case 0xFF: /* revision */
+ break;
+
+ default:
+ printf("labx-audio-depacketizer: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps depacketizer_regs_ops = {
+ .read = depacketizer_regs_read,
+ .write = depacketizer_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Clock domain registers
+ */
+static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ uint32_t retval = 0;
+ int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x10) {
+ case 0x00: /* recovery index */
+ break;
+
+ case 0x01: /* ts interval */
+ retval = p->clockDomainInfo[domain].tsInterval;
+ break;
+
+ case 0x08: /* DAC offset */
+ break;
+
+ case 0x09: /* DAC P coeff */
+ break;
+
+ case 0x0A: /* lock count */
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ depacketizer_t *p = opaque;
+ uint32_t value = val64;
+ int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x10) {
+ case 0x00: /* recovery index */
+ break;
+
+ case 0x01: /* ts interval */
+ p->clockDomainInfo[domain].tsInterval = value;
+ break;
+
+ case 0x08: /* DAC offset */
+ break;
+
+ case 0x09: /* DAC P coeff */
+ break;
+
+ case 0x0A: /* lock count */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps clock_domain_regs_ops = {
+ .read = clock_domain_regs_read,
+ .write = clock_domain_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ depacketizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_audio_depacketizer_init(SysBusDevice *dev)
+{
+ depacketizer_t *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+ p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
+ p->clockDomains);
+
+ /* Set up the IRQ */
+ sysbus_init_irq(dev, &p->irq);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_depacketizer, &depacketizer_regs_ops, p,
+ "labx,audio-depacketizer-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
+ "labx,audio-depacketizer-cd-regs",
+ 0x10 * 4 * p->clockDomains);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,audio-depacketizer-microcode",
+ 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_depacketizer);
+ sysbus_init_mmio(dev, &p->mmio_clock_domain);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress +
+ (2 << (min_bits(p->microcodeWords-1)+2)));
+
+ if (p->hasDMA) {
+ p->dma = labx_dma_create(p->baseAddress +
+ (4 << (min_bits(p->microcodeWords-1)+2)),
+ 1024);
+ }
+
+ return 0;
+}
+
+static Property labx_audio_depacketizer_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", depacketizer_t, baseAddress, 0),
+ DEFINE_PROP_UINT32("clockDomains", depacketizer_t, clockDomains, 1),
+ DEFINE_PROP_UINT32("cacheDataWords", depacketizer_t, cacheDataWords, 1024),
+ DEFINE_PROP_UINT32("paramWords", depacketizer_t, paramWords, 1024),
+ DEFINE_PROP_UINT32("microcodeWords", depacketizer_t, microcodeWords, 1024),
+ DEFINE_PROP_UINT32("maxStreamSlots", depacketizer_t, maxStreamSlots, 32),
+ DEFINE_PROP_UINT32("maxStreams", depacketizer_t, maxStreams, 128),
+ DEFINE_PROP_UINT32("hasDMA", depacketizer_t, hasDMA, 1),
+ DEFINE_PROP_UINT32("matchArch", depacketizer_t, matchArch, 255),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_audio_depacketizer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_audio_depacketizer_init;
+ dc->props = labx_audio_depacketizer_properties;
+}
+
+static TypeInfo labx_audio_depacketizer_info = {
+ .name = "labx,audio-depacketizer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(depacketizer_t),
+ .class_init = labx_audio_depacketizer_class_init,
+};
+
+static void labx_audio_depacketizer_register(void)
+{
+ type_register_static(&labx_audio_depacketizer_info);
+}
+
+type_init(labx_audio_depacketizer_register)
+
diff --git a/hw/labx_audio_packetizer.c b/hw/labx_audio_packetizer.c
new file mode 100644
index 0000000..120cce0
--- /dev/null
+++ b/hw/labx_audio_packetizer.c
@@ -0,0 +1,397 @@
+
+/*
+ * QEMU model of the LabX audio packetizer.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+struct clock_domain_info {
+ uint32_t tsInterval;
+ uint32_t domainEnabled;
+};
+
+typedef struct audio_packetizer {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_packetizer;
+ MemoryRegion mmio_clock_domain;
+ MemoryRegion mmio_template;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t clockDomains;
+ uint32_t cacheDataWords;
+ uint32_t templateWords;
+ uint32_t microcodeWords;
+ uint32_t shaperFractionBits;
+ uint32_t maxStreamSlots;
+ uint32_t dualOutput;
+
+ /* IRQ */
+ qemu_irq irq;
+
+ /* Values set by drivers */
+ uint32_t tsOffset;
+ uint32_t sendSlope;
+ uint32_t idleSlope;
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+
+ /* Template buffer */
+ uint32_t *templateRam;
+
+ /* Clock domain information */
+ struct clock_domain_info *clockDomainInfo;
+} packetizer_t;
+
+/*
+ * Packetizer registers
+ */
+static uint64_t packetizer_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* start vector */
+ break;
+
+ case 0x02: /* ts offset */
+ break;
+
+ case 0x03: /* irq mask */
+ break;
+
+ case 0x04: /* irq flags */
+ break;
+
+ case 0x05: /* sync reg */
+ break;
+
+ case 0x06: /* send slope */
+ break;
+
+ case 0x07: /* idle slope */
+ break;
+
+ case 0xFD: /* capabilities a */
+ retval = (p->maxStreamSlots & 0x7F) | ((p->dualOutput) ? 0x80 : 0x00);
+ break;
+
+ case 0xFE: /* capabilities b */
+ retval = ((p->shaperFractionBits & 0x7F) << 24) |
+ ((p->clockDomains & 0xFF) << 16) |
+ ((min_bits(p->templateWords-1) & 0xFF) << 8) |
+ ((min_bits(p->microcodeWords-1) & 0xFF));
+ break;
+
+ case 0xFF: /* revision */
+ retval = 0x00000013;
+ break;
+
+ default:
+ printf("labx-audio-packetizer: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void packetizer_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* start vector */
+ break;
+
+ case 0x02: /* ts offset */
+ p->tsOffset = value;
+ break;
+
+ case 0x03: /* irq mask */
+ break;
+
+ case 0x04: /* irq flags */
+ break;
+
+ case 0x05: /* sync reg */
+ break;
+
+ case 0x06: /* send slope */
+ p->sendSlope = value;
+ break;
+
+ case 0x07: /* idle slope */
+ p->idleSlope = value;
+ break;
+
+ case 0xFD: /* capabilities a */
+ break;
+
+ case 0xFE: /* capabilities b */
+ break;
+
+ case 0xFF: /* revision */
+ break;
+
+ default:
+ printf("labx-audio-packetizer: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps packetizer_regs_ops = {
+ .read = packetizer_regs_read,
+ .write = packetizer_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Clock domain registers
+ */
+static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ uint32_t retval = 0;
+ int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x01) {
+ case 0x00: /* ts interval */
+ retval = p->clockDomainInfo[domain].tsInterval;
+ break;
+
+ case 0x01: /* domain enable */
+ retval = p->clockDomainInfo[domain].domainEnabled;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x01) {
+ case 0x00: /* ts interval */
+ p->clockDomainInfo[domain].tsInterval = value;
+ break;
+
+ case 0x01: /* domain enable */
+ p->clockDomainInfo[domain].domainEnabled = value;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps clock_domain_regs_ops = {
+ .read = clock_domain_regs_read,
+ .write = clock_domain_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Template RAM
+ */
+static uint64_t template_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ return p->templateRam[RAM_INDEX(addr, p->templateWords)];
+}
+
+static void template_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->templateRam[RAM_INDEX(addr, p->templateWords)] = value;
+}
+
+static const MemoryRegionOps template_ram_ops = {
+ .read = template_ram_read,
+ .write = template_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static int labx_audio_packetizer_init(SysBusDevice *dev)
+{
+ packetizer_t *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->tsOffset = 0x00000000;
+ p->sendSlope = 0x00000000;
+ p->idleSlope = 0x00000000;
+ p->templateRam = g_malloc0(p->templateWords*4);
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+ p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
+ p->clockDomains);
+
+ /* Set up the IRQ */
+ sysbus_init_irq(dev, &p->irq);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_packetizer, &packetizer_regs_ops, p,
+ "labx,audio-packetizer-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
+ "labx,audio-packetizer-cd-regs",
+ 2 * 4 * p->clockDomains);
+ memory_region_init_io(&p->mmio_template, &template_ram_ops, p,
+ "labx,audio-packetizer-template",
+ 4 * p->templateWords);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,audio-packetizer-microcode",
+ 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_packetizer);
+ sysbus_init_mmio(dev, &p->mmio_clock_domain);
+ sysbus_init_mmio(dev, &p->mmio_template);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress +
+ (2 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 3, p->baseAddress +
+ (3 << (min_bits(p->microcodeWords-1)+2)));
+
+ return 0;
+}
+
+static Property labx_audio_packetizer_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", packetizer_t, baseAddress,
+ 0),
+ DEFINE_PROP_UINT32("clockDomains", packetizer_t, clockDomains,
+ 1),
+ DEFINE_PROP_UINT32("cacheDataWords", packetizer_t, cacheDataWords,
+ 1024),
+ DEFINE_PROP_UINT32("templateWords", packetizer_t, templateWords,
+ 1024),
+ DEFINE_PROP_UINT32("microcodeWords", packetizer_t, microcodeWords,
+ 1024),
+ DEFINE_PROP_UINT32("shaperFractionBits", packetizer_t, shaperFractionBits,
+ 16),
+ DEFINE_PROP_UINT32("maxStreamSlots", packetizer_t, maxStreamSlots,
+ 32),
+ DEFINE_PROP_UINT32("dualOutput", packetizer_t, dualOutput,
+ 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_audio_packetizer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_audio_packetizer_init;
+ dc->props = labx_audio_packetizer_properties;
+}
+
+static TypeInfo labx_audio_packetizer_info = {
+ .name = "labx,audio-packetizer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(packetizer_t),
+ .class_init = labx_audio_packetizer_class_init,
+};
+
+static void labx_audio_packetizer_register(void)
+{
+ type_register_static(&labx_audio_packetizer_info);
+}
+
+type_init(labx_audio_packetizer_register)
+
diff --git a/hw/labx_devices.h b/hw/labx_devices.h
new file mode 100644
index 0000000..317341e
--- /dev/null
+++ b/hw/labx_devices.h
@@ -0,0 +1,103 @@
+/*
+ * Lab X device types header.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <net.h>
+
+/* Audio packetizer */
+static inline DeviceState *
+labx_audio_packetizer_create(target_phys_addr_t base, qemu_irq irq,
+ int clockDomains, int cacheDataWords)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,audio-packetizer");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
+ qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* Audio depacketizer */
+static inline DeviceState *
+labx_audio_depacketizer_create(target_phys_addr_t base, qemu_irq irq,
+ int clockDomains, int cacheDataWords, int hasDMA)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,audio-depacketizer");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
+ qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
+ qdev_prop_set_uint32(dev, "hasDMA", hasDMA);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* DMA */
+static inline DeviceState *
+labx_dma_create(target_phys_addr_t base, int microcodeWords)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,dma");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "microcodeWords", microcodeWords);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
+/* Ethernet */
+static inline DeviceState *
+labx_ethernet_create(NICInfo *nd, target_phys_addr_t base, qemu_irq hostIrq,
+ qemu_irq fifoIrq, qemu_irq phyIrq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "labx-ethernet");
+
+ dev = qdev_create(NULL, "labx,ethernet");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, hostIrq);
+ sysbus_connect_irq(s, 1, fifoIrq);
+ sysbus_connect_irq(s, 2, phyIrq);
+
+ return dev;
+}
+
+/* PTP */
+static inline DeviceState *
+labx_ptp_create(target_phys_addr_t base)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,ptp");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
diff --git a/hw/labx_dma.c b/hw/labx_dma.c
new file mode 100644
index 0000000..9d8058c
--- /dev/null
+++ b/hw/labx_dma.c
@@ -0,0 +1,241 @@
+
+/*
+ * QEMU model of the LabX DMA Engine.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+
+struct labx_dma {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_dma;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t paramWords;
+ uint32_t microcodeWords;
+ uint32_t numIndexRegs;
+ uint32_t numChannels;
+ uint32_t numAlus;
+
+ /* Values set by drivers */
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+};
+
+/*
+ * DMA registers
+ */
+static uint64_t dma_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_dma *p = opaque;
+
+ uint32_t retval = 0;
+
+ if ((addr>>2) & 0x80) {
+ /* vector */
+ } else {
+ switch ((addr>>2) & 0x7F) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* channel enable */
+ break;
+
+ case 0x02: /* channel start */
+ break;
+
+ case 0x03: /* channel irq enable */
+ break;
+
+ case 0x04: /* channel irq */
+ break;
+
+ case 0x05: /* sync */
+ break;
+
+ case 0x7E: /* capabilities */
+ retval = ((p->numIndexRegs & 0x0F) << 12) |
+ ((p->numChannels & 0x03) << 10) |
+ ((p->numAlus & 0x03) << 8) |
+ ((min_bits(p->paramWords-1) & 0x0F) << 4) |
+ ((min_bits(p->microcodeWords-1) & 0x0F));
+ break;
+
+ case 0x7F: /* revision */
+ retval = 0x00000011;
+ break;
+
+ default:
+ printf("labx-dma: Read of unknown register %08X\n", addr);
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static void dma_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_dma *p = opaque; */
+ uint32_t value = val64;
+
+ if ((addr>>2) & 0x80) {
+ /* vector */
+ } else {
+ switch ((addr>>2) & 0x7F) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* channel enable */
+ break;
+
+ case 0x02: /* channel start */
+ break;
+
+ case 0x03: /* channel irq enable */
+ break;
+
+ case 0x04: /* channel irq */
+ break;
+
+ case 0x05: /* sync */
+ break;
+
+ case 0x7E: /* capabilities */
+ break;
+
+ case 0x7F: /* revision */
+ break;
+
+ default:
+ printf("labx-dma: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps dma_regs_ops = {
+ .read = dma_regs_read,
+ .write = dma_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_dma *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_dma *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_dma_init(SysBusDevice *dev)
+{
+ struct labx_dma *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_dma, &dma_regs_ops, p,
+ "labx,dma-regs", 0x100 * 4);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,dma-microcode", 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_dma);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+
+ return 0;
+}
+
+static Property labx_dma_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_dma, baseAddress, 0),
+ DEFINE_PROP_UINT32("paramWords", struct labx_dma, paramWords, 1024),
+ DEFINE_PROP_UINT32("microcodeWords", struct labx_dma, microcodeWords, 1024),
+ DEFINE_PROP_UINT32("numIndexRegs", struct labx_dma, numIndexRegs, 4),
+ DEFINE_PROP_UINT32("numChannels", struct labx_dma, numChannels, 1),
+ DEFINE_PROP_UINT32("numAlus", struct labx_dma, numAlus, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_dma_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_dma_init;
+ dc->props = labx_dma_properties;
+}
+
+static TypeInfo labx_dma_info = {
+ .name = "labx,dma",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_dma),
+ .class_init = labx_dma_class_init,
+};
+
+static void labx_dma_register(void)
+{
+ type_register_static(&labx_dma_info);
+}
+
+type_init(labx_dma_register)
+
diff --git a/hw/labx_ethernet.c b/hw/labx_ethernet.c
new file mode 100644
index 0000000..c47c91b
--- /dev/null
+++ b/hw/labx_ethernet.c
@@ -0,0 +1,615 @@
+
+/*
+ * QEMU model of the LabX legacy ethernet core.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "net.h"
+
+#define FIFO_RAM_BYTES 2048
+#define LENGTH_FIFO_WORDS 16
+
+struct labx_ethernet {
+ SysBusDevice busdev;
+ qemu_irq hostIrq;
+ qemu_irq fifoIrq;
+ qemu_irq phyIrq;
+ NICState *nic;
+ NICConf conf;
+
+ MemoryRegion mmio_ethernet;
+ MemoryRegion mmio_mac;
+ MemoryRegion mmio_fifo;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+
+ /* Values set by drivers */
+ uint32_t hostRegs[0x10];
+ uint32_t fifoRegs[0x10];
+
+ /* Tx buffers */
+ uint32_t *txBuffer;
+ uint32_t txPushIndex;
+ uint32_t txPopIndex;
+
+ uint32_t *txLengthBuffer;
+ uint32_t txLengthPushIndex;
+ uint32_t txLengthPopIndex;
+
+ /* Rx buffers */
+ uint32_t *rxBuffer;
+ uint32_t rxPushIndex;
+ uint32_t rxPopIndex;
+
+ uint32_t *rxLengthBuffer;
+ uint32_t rxLengthPushIndex;
+ uint32_t rxLengthPopIndex;
+};
+
+/*
+ * Legacy ethernet registers
+ */
+static void update_host_irq(struct labx_ethernet *p)
+{
+ if ((p->hostRegs[0x03] & p->hostRegs[2]) != 0) {
+ qemu_irq_raise(p->hostIrq);
+ } else {
+ qemu_irq_lower(p->hostIrq);
+ }
+}
+
+static void mdio_xfer(struct labx_ethernet *p, int readWrite,
+ int phyAddr, int regAddr)
+{
+ printf("MDIO %s: addr=%d, reg=%d\n", (readWrite) ? "READ" : "WRITE",
+ phyAddr, regAddr);
+ if (readWrite) {
+ /* TODO: PHY info */
+ p->hostRegs[0x01] = 0x0000FFFF;
+ }
+ p->hostRegs[0x03] |= 1;
+ update_host_irq(p);
+}
+
+static uint64_t ethernet_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* mdio control */
+ case 0x01: /* mdio data */
+ case 0x02: /* irq mask */
+ case 0x03: /* irq flags */
+ case 0x04: /* vlan mask */
+ case 0x05: /* filter select */
+ retval = p->hostRegs[(addr>>2) & 0x0F];
+ break;
+
+ case 0x06: /* filter control */
+ retval = 0x20000000;
+ break;
+
+ case 0x0F: /* revision */
+ retval = 0x00000C13;
+ break;
+
+ case 0x07: /* filter load */
+ retval = p->hostRegs[(addr>>2) & 0x0F];
+ break;
+
+ case 0x08: /* bad packet */
+ retval = 0;
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void ethernet_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* mdio control */
+ p->hostRegs[0x00] = (value & 0x000007FF);
+ mdio_xfer(p, (value >> 10) & 1, (value >> 5) & 0x1F, value & 0x1F);
+ break;
+
+ case 0x01: /* mdio data */
+ p->hostRegs[0x01] = (value & 0x0000FFFF);
+ break;
+
+ case 0x02: /* irq mask */
+ p->hostRegs[0x02] = (value & 0x00000003);
+ update_host_irq(p);
+ break;
+
+ case 0x03: /* irq flags */
+ p->hostRegs[0x03] &= ~(value & 0x00000003);
+ update_host_irq(p);
+ break;
+
+ case 0x04: /* vlan mask */
+ break;
+
+ case 0x05: /* filter select */
+ break;
+
+ case 0x06: /* filter control */
+ break;
+
+ case 0x07: /* filter load */
+ break;
+
+ case 0x08: /* bad packet */
+ break;
+
+ case 0x0F: /* revision */
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps ethernet_regs_ops = {
+ .read = ethernet_regs_read,
+ .write = ethernet_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * MAC registers
+ */
+static uint64_t mac_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ /*struct labx_ethernet *p = opaque; */
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x01: /* host rx config */
+ break;
+
+ case 0x02: /* host tx config */
+ break;
+
+ case 0x04: /* host speed config */
+ break;
+
+ case 0x05: /* host mdio config */
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown mac register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void mac_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_ethernet *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x01: /* host rx config */
+ break;
+
+ case 0x02: /* host tx config */
+ break;
+
+ case 0x04: /* host speed config */
+ break;
+
+ case 0x05: /* host mdio config */
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown mac register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps mac_regs_ops = {
+ .read = mac_regs_read,
+ .write = mac_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * FIFO registers
+ */
+
+#define FIFO_INT_STATUS_ADDRESS 0x0
+#define FIFO_INT_ENABLE_ADDRESS 0x1
+# define FIFO_INT_RPURE 0x80000000
+# define FIFO_INT_RPORE 0x40000000
+# define FIFO_INT_RPUE 0x20000000
+# define FIFO_INT_TPOE 0x10000000
+# define FIFO_INT_TC 0x08000000
+# define FIFO_INT_RC 0x04000000
+# define FIFO_INT_MASK 0xFC000000
+#define FIFO_TX_RESET_ADDRESS 0x2
+# define FIFO_RESET_MAGIC 0xA5
+#define FIFO_TX_VACANCY_ADDRESS 0x3
+#define FIFO_TX_DATA_ADDRESS 0x4
+#define FIFO_TX_LENGTH_ADDRESS 0x5
+#define FIFO_RX_RESET_ADDRESS 0x6
+#define FIFO_RX_OCCUPANCY_ADDRESS 0x7
+#define FIFO_RX_DATA_ADDRESS 0x8
+#define FIFO_RX_LENGTH_ADDRESS 0x9
+
+static void update_fifo_irq(struct labx_ethernet *p)
+{
+ if ((p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &
+ p->fifoRegs[FIFO_INT_ENABLE_ADDRESS]) != 0) {
+ qemu_irq_raise(p->fifoIrq);
+ } else {
+ qemu_irq_lower(p->fifoIrq);
+ }
+}
+
+static void send_packet(struct labx_ethernet *p)
+{
+ while (p->txLengthPopIndex != p->txLengthPushIndex) {
+ int i;
+ uint32_t packetBuf[512];
+
+ int length = p->txLengthBuffer[p->txLengthPopIndex];
+ p->txLengthPopIndex = (p->txLengthPopIndex + 1) % LENGTH_FIFO_WORDS;
+
+ for (i = 0; i < ((length+3)/4); i++) {
+ packetBuf[i] = be32_to_cpu(p->txBuffer[p->txPopIndex]);
+ p->txPopIndex = (p->txPopIndex + 1) % (FIFO_RAM_BYTES/4);
+ }
+
+ qemu_send_packet(&p->nic->nc, (void *)packetBuf, length);
+ }
+
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TC;
+ update_fifo_irq(p);
+}
+
+static uint64_t fifo_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case FIFO_INT_STATUS_ADDRESS:
+ case FIFO_INT_ENABLE_ADDRESS:
+ case FIFO_TX_RESET_ADDRESS:
+ retval = p->fifoRegs[(addr>>2) & 0x0F];
+ break;
+
+ case FIFO_TX_VACANCY_ADDRESS:
+ retval = (p->txPopIndex - p->txPushIndex) - 1;
+ if ((int32_t)retval < 0) {
+ retval += (FIFO_RAM_BYTES/4);
+ }
+
+ if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) {
+ /* Full length fifo */
+ retval = 0;
+ }
+ break;
+
+ case FIFO_TX_DATA_ADDRESS:
+ case FIFO_TX_LENGTH_ADDRESS:
+ case FIFO_RX_RESET_ADDRESS:
+ retval = p->fifoRegs[(addr>>2) & 0x0F];
+ break;
+
+ case FIFO_RX_OCCUPANCY_ADDRESS:
+ retval = p->rxPushIndex - p->rxPopIndex;
+ if ((int32_t)retval < 0) {
+ retval += (FIFO_RAM_BYTES/4);
+ }
+ break;
+
+ case FIFO_RX_DATA_ADDRESS:
+ retval = p->rxBuffer[p->rxPopIndex];
+ if (p->rxPopIndex != p->rxPushIndex) {
+ p->rxPopIndex = (p->rxPopIndex+1) % (FIFO_RAM_BYTES/4);
+ } else {
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
+ update_fifo_irq(p);
+ }
+ break;
+
+ case FIFO_RX_LENGTH_ADDRESS:
+ retval = p->rxLengthBuffer[p->rxLengthPopIndex];
+ if (p->rxLengthPopIndex != p->rxLengthPushIndex) {
+ p->rxLengthPopIndex = (p->rxLengthPopIndex+1) % LENGTH_FIFO_WORDS;
+ } else {
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
+ update_fifo_irq(p);
+ }
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown fifo register %08X\n", addr);
+ break;
+ }
+
+ /* printf("FIFO REG READ %08X (%d) = %08X\n",
+ addr, (addr>>2) & 0x0F, retval); */
+
+ return retval;
+}
+
+static void fifo_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+ uint32_t value = val64;
+
+ /* printf("FIFO REG WRITE %08X (%d) = %08X\n",
+ addr, (addr>>2) & 0x0F, value); */
+
+ switch ((addr>>2) & 0x0F) {
+ case FIFO_INT_STATUS_ADDRESS:
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &= ~(value & FIFO_INT_MASK);
+ update_fifo_irq(p);
+ break;
+
+ case FIFO_INT_ENABLE_ADDRESS:
+ p->fifoRegs[FIFO_INT_ENABLE_ADDRESS] = (value & FIFO_INT_MASK);
+ update_fifo_irq(p);
+ break;
+
+ case FIFO_TX_RESET_ADDRESS:
+ if (value == FIFO_RESET_MAGIC) {
+ p->txPushIndex = 0;
+ p->txPopIndex = 0;
+ p->txLengthPushIndex = 0;
+ p->txLengthPopIndex = 0;
+ }
+ break;
+
+ case FIFO_TX_VACANCY_ADDRESS:
+ break;
+
+ case FIFO_TX_DATA_ADDRESS:
+ if ((((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) ||
+ (((p->txPushIndex + 1) % (FIFO_RAM_BYTES/4)) == p->txPopIndex)) {
+ /* Full length fifo or data fifo */
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
+ update_fifo_irq(p);
+ } else {
+ /* Push back the data */
+ p->txBuffer[p->txPushIndex] = value;
+ p->txPushIndex = (p->txPushIndex + 1) % (FIFO_RAM_BYTES/4);
+ }
+ break;
+
+ case FIFO_TX_LENGTH_ADDRESS:
+ if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) {
+ /* Full length fifo */
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
+ update_fifo_irq(p);
+ } else {
+ /* Push back the length */
+ p->txLengthBuffer[p->txLengthPushIndex] = value;
+ p->txLengthPushIndex = (p->txLengthPushIndex + 1) %
+ LENGTH_FIFO_WORDS;
+ send_packet(p);
+ }
+ break;
+
+ case FIFO_RX_RESET_ADDRESS:
+ if (value == FIFO_RESET_MAGIC) {
+ p->rxPushIndex = 0;
+ p->rxPopIndex = 0;
+ p->rxLengthPushIndex = 0;
+ p->rxLengthPopIndex = 0;
+ }
+ break;
+
+ case FIFO_RX_OCCUPANCY_ADDRESS:
+ break;
+
+ case FIFO_RX_DATA_ADDRESS:
+ break;
+
+ case FIFO_RX_LENGTH_ADDRESS:
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown fifo register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps fifo_regs_ops = {
+ .read = fifo_regs_read,
+ .write = fifo_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int eth_can_rx(NetClientState *nc)
+{
+ /*struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque; */
+
+ return 1;
+}
+
+static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ struct labx_ethernet *p = DO_UPCAST(NICState, nc, nc)->opaque;
+ int i;
+ const uint32_t *wbuf = (const uint32_t *)buf;
+ int rxPushIndexStart = p->rxPushIndex;
+
+ for (i = 0; i < ((size+3)/4); i++) {
+ p->rxBuffer[p->rxPushIndex] = cpu_to_be32(wbuf[i]);
+ p->rxPushIndex = (p->rxPushIndex + 1) % (FIFO_RAM_BYTES/4);
+ if (p->rxPushIndex == p->rxPopIndex) {
+ /* Packet didn't fit */
+ p->rxPushIndex = rxPushIndexStart;
+ return -1;
+ }
+ }
+
+ if ((p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS == p->rxLengthPopIndex) {
+ /* Length didn't fit */
+ p->rxPushIndex = rxPushIndexStart;
+ return -1;
+ }
+
+ p->rxLengthBuffer[p->rxLengthPushIndex] = size;
+ p->rxLengthPushIndex = (p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS;
+
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RC;
+ update_fifo_irq(p);
+
+ return size;
+}
+
+static void eth_cleanup(NetClientState *nc)
+{
+ struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_labx_ethernet_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_rx,
+ .receive = eth_rx,
+ .cleanup = eth_cleanup,
+};
+
+static int labx_ethernet_init(SysBusDevice *dev)
+{
+ struct labx_ethernet *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->txBuffer = g_malloc0(FIFO_RAM_BYTES);
+ p->txLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
+ p->rxBuffer = g_malloc0(FIFO_RAM_BYTES);
+ p->rxLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
+
+ p->txPushIndex = 0;
+ p->txPopIndex = 0;
+ p->txLengthPushIndex = 0;
+ p->txLengthPopIndex = 0;
+ p->rxPushIndex = 0;
+ p->rxPopIndex = 0;
+ p->rxLengthPushIndex = 0;
+ p->rxLengthPopIndex = 0;
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_ethernet, ðernet_regs_ops, p,
+ "labx,ethernet-regs", 0x10 * 4);
+ memory_region_init_io(&p->mmio_mac, &mac_regs_ops, p,
+ "labx,ethernet-mac-regs", 0x10 * 4);
+ memory_region_init_io(&p->mmio_fifo, &fifo_regs_ops, p,
+ "labx,ethernet-fifo-regs", 0x10 * 4);
+
+ sysbus_init_mmio(dev, &p->mmio_ethernet);
+ sysbus_init_mmio(dev, &p->mmio_mac);
+ sysbus_init_mmio(dev, &p->mmio_fifo);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress + (1 << (10+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress + (2 << (10+2)));
+
+ /* Initialize the irqs */
+ sysbus_init_irq(dev, &p->hostIrq);
+ sysbus_init_irq(dev, &p->fifoIrq);
+ sysbus_init_irq(dev, &p->phyIrq);
+
+ /* Set up the NIC */
+ qemu_macaddr_default_if_unset(&p->conf.macaddr);
+ p->nic = qemu_new_nic(&net_labx_ethernet_info, &p->conf,
+ object_get_typename(OBJECT(p)), dev->qdev.id, p);
+ qemu_format_nic_info_str(&p->nic->nc, p->conf.macaddr.a);
+ return 0;
+}
+
+static Property labx_ethernet_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_ethernet, baseAddress, 0),
+ DEFINE_NIC_PROPERTIES(struct labx_ethernet, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_ethernet_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_ethernet_init;
+ dc->props = labx_ethernet_properties;
+}
+
+static TypeInfo labx_ethernet_info = {
+ .name = "labx,ethernet",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_ethernet),
+ .class_init = labx_ethernet_class_init,
+};
+
+static void labx_ethernet_register(void)
+{
+ type_register_static(&labx_ethernet_info);
+}
+
+type_init(labx_ethernet_register)
+
diff --git a/hw/labx_ptp.c b/hw/labx_ptp.c
new file mode 100644
index 0000000..68d4b54
--- /dev/null
+++ b/hw/labx_ptp.c
@@ -0,0 +1,291 @@
+
+/*
+ * QEMU model of the LabX PTP.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+#define PTP_MAX_PACKETS 8
+#define PTP_MAX_PACKET_BYTES 256
+#define PTP_RAM_BYTES (PTP_MAX_PACKETS*PTP_MAX_PACKET_BYTES)
+#define PTP_HOST_RAM_WORDS (PTP_RAM_BYTES/4)
+
+struct labx_ptp {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_ptp;
+ MemoryRegion mmio_tx;
+ MemoryRegion mmio_rx;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+
+ /* Values set by drivers */
+
+ /* Tx buffers */
+ uint32_t *txRam;
+
+ /* Rx buffers */
+ uint32_t *rxRam;
+};
+
+/*
+ * PTP registers
+ */
+static uint64_t ptp_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ /*struct labx_ptp *p = opaque; */
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* rx */
+ break;
+
+ case 0x01: /* tx */
+ break;
+
+ case 0x02: /* irq mask */
+ break;
+
+ case 0x03: /* irq flags */
+ break;
+
+ case 0x04: /* rtc increment */
+ break;
+
+ case 0x05: /* seconds high */
+ break;
+
+ case 0x06: /* seconds low */
+ break;
+
+ case 0x07: /* nanoseconds */
+ break;
+
+ case 0x08: /* timer */
+ break;
+
+ case 0x09: /* local seconds high */
+ break;
+
+ case 0x0A: /* local seconds low */
+ break;
+
+ case 0x0B: /* local nanoseconds */
+ break;
+
+ case 0x0F: /* revision */
+ retval = 0x00000111; /* Report 1 port, revision 1.1 */
+ break;
+
+ default:
+ printf("labx-ptp: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void ptp_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_ptp *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* rx */
+ break;
+
+ case 0x01: /* tx */
+ break;
+
+ case 0x02: /* irq mask */
+ break;
+
+ case 0x03: /* irq flags */
+ break;
+
+ case 0x04: /* rtc increment */
+ break;
+
+ case 0x05: /* seconds high */
+ break;
+
+ case 0x06: /* seconds low */
+ break;
+
+ case 0x07: /* nanoseconds */
+ break;
+
+ case 0x08: /* timer */
+ break;
+
+ case 0x09: /* local seconds high */
+ break;
+
+ case 0x0A: /* local seconds low */
+ break;
+
+ case 0x0B: /* local nanoseconds */
+ break;
+
+ case 0x0F: /* revision */
+ break;
+
+ default:
+ printf("labx-ptp: Write of unknown register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps ptp_regs_ops = {
+ .read = ptp_regs_read,
+ .write = ptp_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Tx Ram
+ */
+static uint64_t tx_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+
+ return p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
+}
+
+static void tx_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+ uint32_t value = val64;
+
+ p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
+}
+
+static const MemoryRegionOps tx_ram_ops = {
+ .read = tx_ram_read,
+ .write = tx_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Rx Ram
+ */
+static uint64_t rx_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+
+ return p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
+}
+
+static void rx_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+ uint32_t value = val64;
+
+ p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
+}
+
+static const MemoryRegionOps rx_ram_ops = {
+ .read = rx_ram_read,
+ .write = rx_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_ptp_init(SysBusDevice *dev)
+{
+ struct labx_ptp *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->txRam = g_malloc0(PTP_RAM_BYTES);
+ p->rxRam = g_malloc0(PTP_RAM_BYTES);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_ptp, &ptp_regs_ops, p, "labx,ptp-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_tx, &tx_ram_ops, p, "labx,ptp-tx",
+ PTP_RAM_BYTES);
+ memory_region_init_io(&p->mmio_rx, &rx_ram_ops, p, "labx,ptp-rx",
+ PTP_RAM_BYTES);
+
+ sysbus_init_mmio(dev, &p->mmio_ptp);
+ sysbus_init_mmio(dev, &p->mmio_tx);
+ sysbus_init_mmio(dev, &p->mmio_rx);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress + (1 << min_bits(PTP_RAM_BYTES-1)));
+ sysbus_mmio_map(dev, 2, p->baseAddress + (2 << min_bits(PTP_RAM_BYTES-1)));
+
+ return 0;
+}
+
+static Property labx_ptp_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_ptp, baseAddress, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_ptp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_ptp_init;
+ dc->props = labx_ptp_properties;
+}
+
+static TypeInfo labx_ptp_info = {
+ .name = "labx,ptp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_ptp),
+ .class_init = labx_ptp_class_init,
+};
+
+static void labx_ptp_register(void)
+{
+ type_register_static(&labx_ptp_info);
+}
+
+type_init(labx_ptp_register)
+
--
1.7.9.5
next prev parent reply other threads:[~2012-09-10 0:19 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <Nios2-Resend2>
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
2012-09-11 20:19 ` Blue Swirl
2012-09-14 3:30 ` Chris Wulff
2012-09-11 21:34 ` Aurelien Jarno
2012-09-11 22:30 ` Richard Henderson
2012-09-11 23:18 ` Richard Henderson
2012-09-14 3:42 ` Chris Wulff
2012-09-15 15:33 ` Chris Wulff
2012-09-15 14:55 ` Andreas Färber
2012-09-10 0:20 ` [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB crwulff
2012-09-11 19:58 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII crwulff
2012-09-11 19:53 ` Blue Swirl
2012-09-15 15:06 ` Andreas Färber
2012-09-10 0:20 ` crwulff [this message]
2012-09-11 20:22 ` [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children crwulff
2012-09-12 0:12 ` Peter Crosthwaite
2012-09-10 0:20 ` [Qemu-devel] [PATCH 6/9] NiosII: Build system and documentation integration crwulff
2012-09-10 0:20 ` [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file crwulff
2012-09-11 19:40 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 8/9] MicroBlaze: " crwulff
2012-09-11 19:27 ` Blue Swirl
2012-09-12 0:17 ` Peter Crosthwaite
2012-09-14 19:13 ` Blue Swirl
2012-09-11 23:59 ` Peter Crosthwaite
2012-09-14 4:01 ` Chris Wulff
2012-09-10 0:20 ` [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled crwulff
2012-09-12 0:25 ` Peter Crosthwaite
2012-09-11 23:40 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support Peter Crosthwaite
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1347236407-10465-5-git-send-email-crwulff@gmail.com \
--to=crwulff@gmail.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.