From patchwork Mon Sep 10 00:20:02 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: crwulff@gmail.com X-Patchwork-Id: 182770 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 0C6CF2C0096 for ; Mon, 10 Sep 2012 11:25:08 +1000 (EST) Received: from localhost ([::1]:39557 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TArl6-0007Va-7e for incoming@patchwork.ozlabs.org; Sun, 09 Sep 2012 20:21:48 -0400 Received: from eggs.gnu.org ([208.118.235.92]:52176) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TArj5-0006HI-3V for qemu-devel@nongnu.org; Sun, 09 Sep 2012 20:19:48 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TAriz-0001zW-4U for qemu-devel@nongnu.org; Sun, 09 Sep 2012 20:19:43 -0400 Received: from mail-vc0-f173.google.com ([209.85.220.173]:58133) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TAriy-0001zH-Sf for qemu-devel@nongnu.org; Sun, 09 Sep 2012 20:19:37 -0400 Received: by mail-vc0-f173.google.com with SMTP id fy7so615989vcb.4 for ; Sun, 09 Sep 2012 17:19:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=XKoJqTMfptCzYUyxMyjzIkLys1Qyr8H0YOf/jO+IA/M=; b=UBR6Mr9GpRGSoxUDkY8P2ohRE40mwpbXOWFOxCD5t0GaxDyplYpSHrm5YHCATDT9p6 OqNG/1YTXKTwoSsDas3Wtp2xPrDNgpz4QV4HKvzrdRSm72+USfTjYv8bBpynyX+wziSP 4L+AW1eHYJ29zkV33rGQbLnfLKCMYnbt2QvMSGsAIPCycMIdGg8YBqwYZAu5yMgnxMBg 0LjTDT2qbj5ga6gl2EWU5/GatQTwtrBCkCbkmabrSITuJNYT/oZe2+W+8cKON3IWvhdi fYVgztReutwDOD0kK4RQso//qrGCg5vodjrkUZaVtuUCFrpV8srJxSibaPKRnLA1LLUx 02zw== Received: by 10.52.70.116 with SMTP id l20mr13748895vdu.74.1347236376634; Sun, 09 Sep 2012 17:19:36 -0700 (PDT) Received: from localhost.localdomain (cpe-67-244-159-59.rochester.res.rr.com. [67.244.159.59]) by mx.google.com with ESMTPS id l8sm7515390veu.6.2012.09.09.17.19.34 (version=SSLv3 cipher=OTHER); Sun, 09 Sep 2012 17:19:35 -0700 (PDT) From: crwulff@gmail.com To: qemu-devel@nongnu.org Date: Sun, 9 Sep 2012 20:20:02 -0400 Message-Id: <1347236407-10465-5-git-send-email-crwulff@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1347236407-10465-1-git-send-email-crwulff@gmail.com> References: <1347236407-10465-1-git-send-email-crwulff@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.220.173 X-Mailman-Approved-At: Sun, 09 Sep 2012 20:21:41 -0400 Cc: Chris Wulff Subject: [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Chris Wulff Signed-off-by: Chris Wulff --- 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 + * + */ + +#include "sysbus.h" +#include "sysemu.h" +#include "labx_devices.h" + +#define min_bits qemu_fls +#define RAM_INDEX(addr, size) (((addr)>>2)&((1< 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<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<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 + * + */ + +#include "sysbus.h" +#include "sysemu.h" + +#define min_bits qemu_fls +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<>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<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<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 + * + */ + +#include + +/* 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 + * + */ + +#include "sysbus.h" +#include "sysemu.h" + +#define min_bits qemu_fls +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<>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 + * + */ + +#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 + * + */ + +#include "sysbus.h" +#include "sysemu.h" + +#define min_bits qemu_fls +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<>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) +