diff mbox

[4/9] LabX: Support for some Lab X FPGA devices.

Message ID 1347236407-10465-5-git-send-email-crwulff@gmail.com
State New
Headers show

Commit Message

crwulff@gmail.com Sept. 10, 2012, 12:20 a.m. UTC
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

Comments

Blue Swirl Sept. 11, 2012, 8:22 p.m. UTC | #1
On Mon, Sep 10, 2012 at 12:20 AM,  <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>

Same comments as to other patches. Pointers to chip set documentation
would be nice.

> ---
>  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,    &microcode_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,    &microcode_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, &microcode_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, &ethernet_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
>
>
diff mbox

Patch

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,    &microcode_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,    &microcode_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, &microcode_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, &ethernet_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)
+