Patchwork [3/9] Altera: Add support for Altera devices required to boot linux on NiosII.

login
register
mail settings
Submitter crwulff@gmail.com
Date Sept. 10, 2012, 12:20 a.m.
Message ID <1347236407-10465-4-git-send-email-crwulff@gmail.com>
Download mbox | patch
Permalink /patch/182764/
State New
Headers show

Comments

crwulff@gmail.com - Sept. 10, 2012, 12:20 a.m.
From: Chris Wulff <crwulff@gmail.com>

Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
 hw/Makefile.objs   |    5 ++
 hw/altera.h        |   34 ++++++++
 hw/altera_timer.c  |  198 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/altera_uart.c   |  218 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/altera_vic.c    |  195 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/nios2.h         |   46 +++++++++++
 hw/nios2_pic_cpu.c |   48 ++++++++++++
 7 files changed, 744 insertions(+)
 create mode 100644 hw/altera.h
 create mode 100644 hw/altera_timer.c
 create mode 100644 hw/altera_uart.c
 create mode 100644 hw/altera_vic.c
 create mode 100644 hw/nios2.h
 create mode 100644 hw/nios2_pic_cpu.c
Blue Swirl - Sept. 11, 2012, 7:53 p.m.
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>
> ---
>  hw/Makefile.objs   |    5 ++
>  hw/altera.h        |   34 ++++++++
>  hw/altera_timer.c  |  198 +++++++++++++++++++++++++++++++++++++++++++++++
>  hw/altera_uart.c   |  218 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/altera_vic.c    |  195 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/nios2.h         |   46 +++++++++++
>  hw/nios2_pic_cpu.c |   48 ++++++++++++
>  7 files changed, 744 insertions(+)
>  create mode 100644 hw/altera.h
>  create mode 100644 hw/altera_timer.c
>  create mode 100644 hw/altera_uart.c
>  create mode 100644 hw/altera_vic.c
>  create mode 100644 hw/nios2.h
>  create mode 100644 hw/nios2_pic_cpu.c
>
> diff --git a/hw/Makefile.objs b/hw/Makefile.objs
> index 6dfebd2..59dd2d5 100644
> --- a/hw/Makefile.objs
> +++ b/hw/Makefile.objs
> @@ -67,6 +67,11 @@ hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
>  hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
>  hw-obj-$(CONFIG_XILINX_AXI) += stream.o
>
> +# Altera devices
> +hw-obj-$(CONFIG_ALTERA) += altera_vic.o
> +hw-obj-$(CONFIG_ALTERA) += altera_uart.o
> +hw-obj-$(CONFIG_ALTERA) += altera_timer.o
> +
>  # PKUnity SoC devices
>  hw-obj-$(CONFIG_PUV3) += puv3_intc.o
>  hw-obj-$(CONFIG_PUV3) += puv3_ost.o
> diff --git a/hw/altera.h b/hw/altera.h
> new file mode 100644
> index 0000000..b25366e
> --- /dev/null
> +++ b/hw/altera.h
> @@ -0,0 +1,34 @@
> +/*
> + * Altera Nios II device instantiation header.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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>
> + */
> +
> +/* timer */
> +static inline DeviceState *altera_timer_create(target_phys_addr_t base,
> +                                               qemu_irq irq, int freq)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "altera,timer");
> +    qdev_prop_set_uint32(dev, "frequency", freq);
> +    qdev_init_nofail(dev);
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> +    return dev;
> +}
> +
> diff --git a/hw/altera_timer.c b/hw/altera_timer.c
> new file mode 100644
> index 0000000..e490da5
> --- /dev/null
> +++ b/hw/altera_timer.c
> @@ -0,0 +1,198 @@
> +/*
> + * QEMU model of the Altera timer.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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 "ptimer.h"
> +
> +#define R_STATUS     0
> +#define R_CONTROL    1
> +#define R_PERIODL    2
> +#define R_PERIODH    3
> +#define R_SNAPL      4
> +#define R_SNAPH      5
> +#define R_MAX        6
> +
> +#define STATUS_TO  0x0001
> +#define STATUS_RUN 0x0002
> +
> +#define CONTROL_ITO   0x0001
> +#define CONTROL_CONT  0x0002
> +#define CONTROL_START 0x0004
> +#define CONTROL_STOP  0x0008
> +
> +struct altera_timer {

CamelCase

> +    SysBusDevice  busdev;
> +    MemoryRegion  mmio;
> +    qemu_irq      irq;
> +    uint32_t      freq_hz;
> +    QEMUBH       *bh;
> +    ptimer_state *ptimer;
> +    uint32_t      regs[R_MAX];
> +};
> +
> +static uint64_t timer_read(void *opaque, target_phys_addr_t addr,
> +                           unsigned int size)
> +{
> +    struct altera_timer *t = opaque;
> +    uint32_t r = 0;

uint32_t does not match uint64_t for function return value.

> +
> +    addr >>= 2;
> +    addr &= 0x7;
> +    switch (addr) {
> +    case R_STATUS:
> +        r = t->regs[R_STATUS];
> +        break;
> +
> +    default:
> +        if (addr < ARRAY_SIZE(t->regs)) {
> +            r = t->regs[addr];
> +        }
> +        break;
> +    }
> +
> +    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);

It would be better to set the IRQ only if the state has changed.

> +
> +    return r;
> +}
> +
> +static void timer_start(struct altera_timer *t)
> +{
> +    ptimer_stop(t->ptimer);
> +    ptimer_set_count(t->ptimer, (t->regs[R_PERIODH]<<16) | t->regs[R_PERIODL]);
> +    ptimer_run(t->ptimer, 1);
> +}
> +
> +static void timer_write(void *opaque, target_phys_addr_t addr,
> +                        uint64_t val64, unsigned int size)
> +{
> +    struct altera_timer *t = opaque;
> +    uint32_t value = val64;
> +    uint32_t count = 0;
> +
> +    addr >>= 2;
> +    addr &= 0x7;
> +    switch (addr) {
> +    case R_STATUS:
> +        /* Writing zero clears the timeout */
> +        t->regs[R_STATUS] &= ~STATUS_TO;
> +        break;
> +
> +    case R_CONTROL:
> +        t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT);
> +        if ((value & CONTROL_START) &&
> +            ((t->regs[R_STATUS] & STATUS_RUN) == 0)) {
> +            timer_start(t);
> +        }
> +        if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) {
> +            ptimer_stop(t->ptimer);
> +        }
> +        break;
> +
> +    case R_PERIODL:
> +    case R_PERIODH:
> +        t->regs[addr] = value & 0xFFFF;
> +        if (t->regs[R_STATUS] & STATUS_RUN) {
> +            timer_start(t);
> +        }
> +        break;
> +
> +    case R_SNAPL:
> +    case R_SNAPH:
> +        count = ptimer_get_count(t->ptimer);
> +        t->regs[R_SNAPL] = count & 0xFFFF;
> +        t->regs[R_SNAPH] = (count>>16) & 0xFFFF;
> +        break;
> +
> +    default:
> +        if (addr < ARRAY_SIZE(t->regs)) {
> +            t->regs[addr] = value;
> +        }
> +        break;
> +    }
> +
> +    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
> +}
> +
> +static const MemoryRegionOps timer_ops = {
> +    .read = timer_read,
> +    .write = timer_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Native endian cases should be rare, you should fix it to LE or BE.

> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void timer_hit(void *opaque)
> +{
> +    struct altera_timer *t = opaque;
> +    t->regs[R_STATUS] |= STATUS_TO;
> +
> +    if (t->regs[R_CONTROL] & CONTROL_CONT) {
> +        timer_start(t);
> +    }
> +    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
> +}
> +
> +static int altera_timer_init(SysBusDevice *dev)
> +{
> +    struct altera_timer *t = FROM_SYSBUS(typeof(*t), dev);
> +
> +    sysbus_init_irq(dev, &t->irq);
> +
> +    t->bh = qemu_bh_new(timer_hit, t);
> +    t->ptimer = ptimer_init(t->bh);
> +    ptimer_set_freq(t->ptimer, t->freq_hz);
> +
> +    memory_region_init_io(&t->mmio, &timer_ops, t, "altera,timer",
> +                          R_MAX * sizeof(uint32_t));
> +    sysbus_init_mmio(dev, &t->mmio);
> +    return 0;
> +}
> +
> +static Property altera_timer_properties[] = {
> +    DEFINE_PROP_UINT32("frequency", struct altera_timer, freq_hz, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_timer_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = altera_timer_init;
> +    dc->props = altera_timer_properties;
> +}
> +
> +static TypeInfo altera_timer_info = {

const

> +    .name          = "altera,timer",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct altera_timer),
> +    .class_init    = altera_timer_class_init,
> +};
> +
> +static void altera_timer_register(void)
> +{
> +    type_register_static(&altera_timer_info);
> +}
> +
> +type_init(altera_timer_register)
> +

newline

> diff --git a/hw/altera_uart.c b/hw/altera_uart.c
> new file mode 100644
> index 0000000..e32651e
> --- /dev/null
> +++ b/hw/altera_uart.c
> @@ -0,0 +1,218 @@
> +/*
> + * QEMU model of the Altera uart.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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 "qemu-char.h"
> +
> +#define R_RXDATA        0
> +#define R_TXDATA        1
> +#define R_STATUS        2
> +#define R_CONTROL       3
> +#define R_DIVISOR       4
> +#define R_ENDOFPACKET   5
> +#define R_MAX           6
> +
> +#define STATUS_PE        0x0001
> +#define STATUS_FE        0x0002
> +#define STATUS_BRK       0x0004
> +#define STATUS_ROE       0x0008
> +#define STATUS_TOE       0x0010
> +#define STATUS_TMT       0x0020
> +#define STATUS_TRDY      0x0040
> +#define STATUS_RRDY      0x0080
> +#define STATUS_E         0x0100
> +#define STATUS_DTCS      0x0400
> +#define STATUS_CTS       0x0800
> +#define STATUS_EOP       0x1000
> +
> +#define CONTROL_IPE      0x0001
> +#define CONTROL_IFE      0x0002
> +#define CONTROL_IBRK     0x0004
> +#define CONTROL_IROE     0x0008
> +#define CONTROL_ITOE     0x0010
> +#define CONTROL_ITMT     0x0020
> +#define CONTROL_ITRDY    0x0040
> +#define CONTROL_IRRDY    0x0080
> +#define CONTROL_IE       0x0100
> +#define CONTROL_TBRK     0x0200
> +#define CONTROL_IDTCS    0x0400
> +#define CONTROL_RTS      0x0800
> +#define CONTROL_IEOP     0x1000
> +
> +struct altera_uart {
> +    SysBusDevice busdev;
> +    MemoryRegion mmio;
> +    CharDriverState *chr;
> +    qemu_irq irq;
> +
> +    uint32_t regs[R_MAX];
> +};
> +
> +static void uart_update_irq(struct altera_uart *s)
> +{
> +    unsigned int irq;
> +
> +    irq = (s->regs[R_STATUS] & s->regs[R_CONTROL] &
> +          (STATUS_PE | STATUS_FE | STATUS_BRK | STATUS_ROE | STATUS_TOE |
> +           STATUS_TMT | STATUS_TRDY | STATUS_RRDY | STATUS_E | STATUS_DTCS));
> +    irq = (irq == 0) ? 0 : 1;
> +    qemu_set_irq(s->irq, irq);
> +}
> +
> +static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
> +                          unsigned int size)
> +{
> +    struct altera_uart *s = opaque;
> +    uint32_t r = 0;
> +    addr >>= 2;
> +    addr &= 0x7;
> +    switch (addr) {
> +    case R_RXDATA:
> +        r = s->regs[R_RXDATA];
> +        s->regs[R_STATUS] &= ~STATUS_RRDY;
> +        uart_update_irq(s);
> +        break;
> +
> +    case R_STATUS:
> +        r = s->regs[R_STATUS];
> +        s->regs[R_STATUS] &= ~(STATUS_PE | STATUS_FE | STATUS_BRK |
> +                               STATUS_ROE | STATUS_TOE | STATUS_E |
> +                               STATUS_DTCS);
> +        uart_update_irq(s);
> +        break;
> +
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            r = s->regs[addr];
> +        }
> +        break;
> +    }
> +
> +    return r;
> +}
> +
> +static void uart_write(void *opaque, target_phys_addr_t addr,
> +                       uint64_t val64, unsigned int size)
> +{
> +    struct altera_uart *s = opaque;
> +    uint32_t value = val64;
> +    unsigned char ch = value;
> +
> +    addr >>= 2;
> +    addr &= 0x7;
> +
> +    switch (addr) {
> +    case R_TXDATA:
> +        if (s->chr) {
> +            qemu_chr_fe_write(s->chr, &ch, 1);
> +        }
> +
> +        s->regs[addr] = value;
> +        break;
> +
> +    case R_RXDATA:
> +    case R_STATUS:
> +        /* No writeable bits */
> +        break;
> +
> +    default:
> +        s->regs[addr] = value;
> +        break;
> +    }
> +    uart_update_irq(s);
> +}
> +
> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> +{
> +    struct altera_uart *s = opaque;
> +
> +    s->regs[R_RXDATA] = *buf;
> +    s->regs[R_STATUS] |= STATUS_RRDY;
> +
> +    uart_update_irq(s);
> +}
> +
> +static int uart_can_rx(void *opaque)
> +{
> +    struct altera_uart *s = opaque;
> +    return ((s->regs[R_STATUS] & STATUS_RRDY) == 0);
> +}
> +
> +static void uart_event(void *opaque, int event)
> +{
> +}
> +
> +static const MemoryRegionOps uart_ops = {
> +    .read = uart_read,
> +    .write = uart_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static int altera_uart_init(SysBusDevice *dev)
> +{
> +    struct altera_uart *s = FROM_SYSBUS(typeof(*s), dev);
> +
> +    s->regs[R_STATUS] = STATUS_TMT | STATUS_TRDY; /* Always ready to tx */
> +
> +    sysbus_init_irq(dev, &s->irq);
> +
> +    memory_region_init_io(&s->mmio, &uart_ops, s,
> +                          "altera,uart", R_MAX * sizeof(uint32_t));
> +    sysbus_init_mmio(dev, &s->mmio);
> +
> +    s->chr = qemu_char_get_next_serial();
> +    if (s->chr) {
> +        qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
> +    }
> +
> +    return 0;
> +}
> +
> +static Property altera_uart_properties[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_uart_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = altera_uart_init;
> +    dc->props = altera_uart_properties;
> +}
> +
> +static TypeInfo altera_uart_info = {
> +    .name          = "altera,uart",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct altera_uart),
> +    .class_init    = altera_uart_class_init,
> +};
> +
> +static void altera_uart_register(void)
> +{
> +    type_register_static(&altera_uart_info);
> +}
> +
> +type_init(altera_uart_register)
> +
> diff --git a/hw/altera_vic.c b/hw/altera_vic.c
> new file mode 100644
> index 0000000..43a2b68
> --- /dev/null
> +++ b/hw/altera_vic.c
> @@ -0,0 +1,195 @@
> +/*
> + * QEMU Altera Vectored Interrupt Controller.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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 "hw.h"
> +
> +#define R_INT_CONFIG_0      0
> +#define R_INT_ENABLE       32
> +#define R_INT_ENABLE_SET   33
> +#define R_INT_ENABLE_CLR   34
> +#define R_INT_PENDING      35
> +#define R_INT_RAW_STATUS   36
> +#define R_SW_INTERRUPT     37
> +#define R_SW_INTERRUPT_SET 38
> +#define R_SW_INTERRUPT_CLR 39
> +#define R_VIC_CONFIG       40
> +#define R_VIC_STATUS       41
> +#define R_VEC_TBL_BASE     42
> +#define R_VEC_TBL_ADDR     43
> +#define R_MAX              44
> +
> +struct altera_vic {
> +    SysBusDevice busdev;
> +    MemoryRegion mmio;
> +    qemu_irq parent_irq;
> +
> +    /* Runtime control registers.  */
> +    uint32_t regs[R_MAX];
> +};
> +
> +static void update_irq(struct altera_vic *pv)
> +{
> +    uint32_t i;
> +    pv->regs[R_INT_PENDING] = (pv->regs[R_INT_RAW_STATUS] |
> +                               pv->regs[R_SW_INTERRUPT]) &
> +                              pv->regs[R_INT_ENABLE];
> +
> +    for (i = 0; i < 32; i++) {
> +        if (pv->regs[R_INT_PENDING] & (1 << i)) {
> +            break;
> +        }
> +    }
> +    if (i == 32) {
> +        pv->regs[R_VEC_TBL_ADDR] = 0;
> +        pv->regs[R_VIC_STATUS] = 0;
> +        qemu_irq_lower(pv->parent_irq);
> +    } else {
> +        pv->regs[R_VEC_TBL_ADDR] = pv->regs[R_VEC_TBL_BASE] +
> +                                   i*(4 << (pv->regs[R_VIC_CONFIG] & 7));

Spaces around '*'.

> +        pv->regs[R_VIC_STATUS] = 0x80000000 | i;
> +        qemu_irq_raise(pv->parent_irq);
> +    }
> +}
> +
> +static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
> +                         unsigned int size)
> +{
> +    struct altera_vic *pv = opaque;
> +    uint32_t r = 0;
> +
> +    addr >>= 2;
> +    if (addr < R_MAX) {
> +        r = pv->regs[addr];
> +    }
> +
> +    return r;
> +}
> +
> +static void pic_write(void *opaque, target_phys_addr_t addr,
> +                      uint64_t val64, unsigned int size)
> +{
> +    struct altera_vic *pv = opaque;
> +    uint32_t value = val64;
> +
> +    addr >>= 2;
> +    if (addr < R_INT_ENABLE) {
> +        /* R_INT_CONFIG_XX */
> +        pv->regs[addr] = value & 0x00001FFF;
> +    } else {
> +        switch (addr) {
> +        case R_INT_PENDING:
> +        case R_INT_RAW_STATUS:
> +        case R_VIC_STATUS:
> +        case R_VEC_TBL_ADDR:
> +            /* read only */
> +            break;
> +
> +        case R_INT_ENABLE_SET:
> +            pv->regs[R_INT_ENABLE] |= value;
> +            break;
> +
> +        case R_SW_INTERRUPT_SET:
> +            pv->regs[R_SW_INTERRUPT] |= value;
> +            break;
> +
> +        case R_INT_ENABLE_CLR:
> +            pv->regs[R_INT_ENABLE] &= ~value;
> +            break;
> +
> +        case R_SW_INTERRUPT_CLR:
> +            pv->regs[R_SW_INTERRUPT] &= ~value;
> +            break;
> +
> +        case R_VIC_CONFIG:
> +            pv->regs[addr] = value & 0x0000000F;
> +            break;
> +
> +        default:
> +            if (addr < ARRAY_SIZE(pv->regs)) {
> +                pv->regs[addr] = value;
> +            }
> +            break;
> +        }
> +    }
> +    update_irq(pv);
> +}
> +
> +static const MemoryRegionOps pic_ops = {
> +    .read = pic_read,
> +    .write = pic_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> +    struct altera_vic *pv = opaque;
> +
> +    pv->regs[R_INT_RAW_STATUS] &= ~(1 << irq);
> +    pv->regs[R_INT_RAW_STATUS] |= level << irq;
> +
> +    update_irq(pv);
> +}
> +
> +static int altera_vic_init(SysBusDevice *dev)
> +{
> +    struct altera_vic *pv = FROM_SYSBUS(typeof(*pv), dev);
> +
> +    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> +    sysbus_init_irq(dev, &pv->parent_irq);
> +
> +    memset(pv->regs, 0, sizeof(uint32_t) * R_MAX);
> +    memory_region_init_io(&pv->mmio, &pic_ops, pv,
> +                          "altera,vic", R_MAX * sizeof(uint32_t));
> +    sysbus_init_mmio(dev, &pv->mmio);
> +    return 0;
> +}
> +
> +static Property altera_vic_properties[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_vic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = altera_vic_init;
> +    dc->props = altera_vic_properties;
> +}
> +
> +static TypeInfo altera_vic_info = {
> +    .name          = "altera,vic",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct altera_vic),
> +    .class_init    = altera_vic_class_init,
> +};
> +
> +static void altera_vic_register(void)
> +{
> +    type_register_static(&altera_vic_info);
> +}
> +
> +type_init(altera_vic_register)
> +
> diff --git a/hw/nios2.h b/hw/nios2.h
> new file mode 100644
> index 0000000..a4af154
> --- /dev/null
> +++ b/hw/nios2.h
> @@ -0,0 +1,46 @@
> +/*
> + * Altera Nios II CPU interrupt controllers
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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>
> + */
> +

Missing duplicate include protection #ifdeffery.

> +#include "sysbus.h"
> +
> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env);
> +
> +static inline DeviceState *
> +altera_vic_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "altera,vic");
> +    qdev_init_nofail(dev);
> +    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> +    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> +    return dev;
> +}
> +
> +static inline DeviceState *
> +altera_iic_create(qemu_irq irq, int kind_of_intr)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "altera,iic");
> +    qdev_init_nofail(dev);
> +    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> +    return dev;
> +}
> diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
> new file mode 100644
> index 0000000..c89b4ae
> --- /dev/null
> +++ b/hw/nios2_pic_cpu.c
> @@ -0,0 +1,48 @@
> +/*
> + * QEMU Altera Nios II CPU interrupt wrapper logic.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * 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 "hw.h"
> +#include "pc.h"
> +#include "nios2.h"
> +
> +void pic_info(Monitor *mon)
> +{
> +}
> +
> +void irq_info(Monitor *mon)
> +{
> +}
> +
> +static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
> +{
> +    CPUNios2State *env = (CPUNios2State *)opaque;

Useless cast in C.

> +    int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
> +
> +    if (level) {
> +        cpu_interrupt(env, type);
> +    } else {
> +        cpu_reset_interrupt(env, type);
> +    }
> +}
> +
> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
> +{
> +    return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
> +}
> --
> 1.7.9.5
>
>
Andreas Färber - Sept. 15, 2012, 3:06 p.m.
Am 11.09.2012 21:53, schrieb Blue Swirl:
> On Mon, Sep 10, 2012 at 12:20 AM,  <crwulff@gmail.com> wrote:
>> diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
>> new file mode 100644
>> index 0000000..c89b4ae
>> --- /dev/null
>> +++ b/hw/nios2_pic_cpu.c
>> @@ -0,0 +1,48 @@
>> +/*
>> + * QEMU Altera Nios II CPU interrupt wrapper logic.
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * 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 "hw.h"

>> +#include "pc.h"

Why?

>> +#include "nios2.h"
>> +
>> +void pic_info(Monitor *mon)
>> +{
>> +}
>> +
>> +void irq_info(Monitor *mon)
>> +{
>> +}

Thought these stubs were no longer necessary...

>> +
>> +static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
>> +{
>> +    CPUNios2State *env = (CPUNios2State *)opaque;
> 
> Useless cast in C.

Please use Nios2CPU so that we can more easily make cpu_interrupt() and
cpu_reset_interrupt() take a CPUState argument in the future.

Please also split this patch up per device and always cc the appropriate
maintainers to facilitate review (e.g.,
--cc-cmd="scripts/get_maintainer.pl --nogit-fallback").

Regards,
Andreas

> 
>> +    int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
>> +
>> +    if (level) {
>> +        cpu_interrupt(env, type);
>> +    } else {
>> +        cpu_reset_interrupt(env, type);
>> +    }
>> +}
>> +
>> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
>> +{
>> +    return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
>> +}

Patch

diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 6dfebd2..59dd2d5 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -67,6 +67,11 @@  hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
 hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
 hw-obj-$(CONFIG_XILINX_AXI) += stream.o
 
+# Altera devices
+hw-obj-$(CONFIG_ALTERA) += altera_vic.o
+hw-obj-$(CONFIG_ALTERA) += altera_uart.o
+hw-obj-$(CONFIG_ALTERA) += altera_timer.o
+
 # PKUnity SoC devices
 hw-obj-$(CONFIG_PUV3) += puv3_intc.o
 hw-obj-$(CONFIG_PUV3) += puv3_ost.o
diff --git a/hw/altera.h b/hw/altera.h
new file mode 100644
index 0000000..b25366e
--- /dev/null
+++ b/hw/altera.h
@@ -0,0 +1,34 @@ 
+/*
+ * Altera Nios II device instantiation header.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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>
+ */
+
+/* timer */
+static inline DeviceState *altera_timer_create(target_phys_addr_t base,
+                                               qemu_irq irq, int freq)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "altera,timer");
+    qdev_prop_set_uint32(dev, "frequency", freq);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
+
diff --git a/hw/altera_timer.c b/hw/altera_timer.c
new file mode 100644
index 0000000..e490da5
--- /dev/null
+++ b/hw/altera_timer.c
@@ -0,0 +1,198 @@ 
+/*
+ * QEMU model of the Altera timer.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 "ptimer.h"
+
+#define R_STATUS     0
+#define R_CONTROL    1
+#define R_PERIODL    2
+#define R_PERIODH    3
+#define R_SNAPL      4
+#define R_SNAPH      5
+#define R_MAX        6
+
+#define STATUS_TO  0x0001
+#define STATUS_RUN 0x0002
+
+#define CONTROL_ITO   0x0001
+#define CONTROL_CONT  0x0002
+#define CONTROL_START 0x0004
+#define CONTROL_STOP  0x0008
+
+struct altera_timer {
+    SysBusDevice  busdev;
+    MemoryRegion  mmio;
+    qemu_irq      irq;
+    uint32_t      freq_hz;
+    QEMUBH       *bh;
+    ptimer_state *ptimer;
+    uint32_t      regs[R_MAX];
+};
+
+static uint64_t timer_read(void *opaque, target_phys_addr_t addr,
+                           unsigned int size)
+{
+    struct altera_timer *t = opaque;
+    uint32_t r = 0;
+
+    addr >>= 2;
+    addr &= 0x7;
+    switch (addr) {
+    case R_STATUS:
+        r = t->regs[R_STATUS];
+        break;
+
+    default:
+        if (addr < ARRAY_SIZE(t->regs)) {
+            r = t->regs[addr];
+        }
+        break;
+    }
+
+    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+
+    return r;
+}
+
+static void timer_start(struct altera_timer *t)
+{
+    ptimer_stop(t->ptimer);
+    ptimer_set_count(t->ptimer, (t->regs[R_PERIODH]<<16) | t->regs[R_PERIODL]);
+    ptimer_run(t->ptimer, 1);
+}
+
+static void timer_write(void *opaque, target_phys_addr_t addr,
+                        uint64_t val64, unsigned int size)
+{
+    struct altera_timer *t = opaque;
+    uint32_t value = val64;
+    uint32_t count = 0;
+
+    addr >>= 2;
+    addr &= 0x7;
+    switch (addr) {
+    case R_STATUS:
+        /* Writing zero clears the timeout */
+        t->regs[R_STATUS] &= ~STATUS_TO;
+        break;
+
+    case R_CONTROL:
+        t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT);
+        if ((value & CONTROL_START) &&
+            ((t->regs[R_STATUS] & STATUS_RUN) == 0)) {
+            timer_start(t);
+        }
+        if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) {
+            ptimer_stop(t->ptimer);
+        }
+        break;
+
+    case R_PERIODL:
+    case R_PERIODH:
+        t->regs[addr] = value & 0xFFFF;
+        if (t->regs[R_STATUS] & STATUS_RUN) {
+            timer_start(t);
+        }
+        break;
+
+    case R_SNAPL:
+    case R_SNAPH:
+        count = ptimer_get_count(t->ptimer);
+        t->regs[R_SNAPL] = count & 0xFFFF;
+        t->regs[R_SNAPH] = (count>>16) & 0xFFFF;
+        break;
+
+    default:
+        if (addr < ARRAY_SIZE(t->regs)) {
+            t->regs[addr] = value;
+        }
+        break;
+    }
+
+    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+}
+
+static const MemoryRegionOps timer_ops = {
+    .read = timer_read,
+    .write = timer_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4
+    }
+};
+
+static void timer_hit(void *opaque)
+{
+    struct altera_timer *t = opaque;
+    t->regs[R_STATUS] |= STATUS_TO;
+
+    if (t->regs[R_CONTROL] & CONTROL_CONT) {
+        timer_start(t);
+    }
+    qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+}
+
+static int altera_timer_init(SysBusDevice *dev)
+{
+    struct altera_timer *t = FROM_SYSBUS(typeof(*t), dev);
+
+    sysbus_init_irq(dev, &t->irq);
+
+    t->bh = qemu_bh_new(timer_hit, t);
+    t->ptimer = ptimer_init(t->bh);
+    ptimer_set_freq(t->ptimer, t->freq_hz);
+
+    memory_region_init_io(&t->mmio, &timer_ops, t, "altera,timer",
+                          R_MAX * sizeof(uint32_t));
+    sysbus_init_mmio(dev, &t->mmio);
+    return 0;
+}
+
+static Property altera_timer_properties[] = {
+    DEFINE_PROP_UINT32("frequency", struct altera_timer, freq_hz, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_timer_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = altera_timer_init;
+    dc->props = altera_timer_properties;
+}
+
+static TypeInfo altera_timer_info = {
+    .name          = "altera,timer",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct altera_timer),
+    .class_init    = altera_timer_class_init,
+};
+
+static void altera_timer_register(void)
+{
+    type_register_static(&altera_timer_info);
+}
+
+type_init(altera_timer_register)
+
diff --git a/hw/altera_uart.c b/hw/altera_uart.c
new file mode 100644
index 0000000..e32651e
--- /dev/null
+++ b/hw/altera_uart.c
@@ -0,0 +1,218 @@ 
+/*
+ * QEMU model of the Altera uart.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 "qemu-char.h"
+
+#define R_RXDATA        0
+#define R_TXDATA        1
+#define R_STATUS        2
+#define R_CONTROL       3
+#define R_DIVISOR       4
+#define R_ENDOFPACKET   5
+#define R_MAX           6
+
+#define STATUS_PE        0x0001
+#define STATUS_FE        0x0002
+#define STATUS_BRK       0x0004
+#define STATUS_ROE       0x0008
+#define STATUS_TOE       0x0010
+#define STATUS_TMT       0x0020
+#define STATUS_TRDY      0x0040
+#define STATUS_RRDY      0x0080
+#define STATUS_E         0x0100
+#define STATUS_DTCS      0x0400
+#define STATUS_CTS       0x0800
+#define STATUS_EOP       0x1000
+
+#define CONTROL_IPE      0x0001
+#define CONTROL_IFE      0x0002
+#define CONTROL_IBRK     0x0004
+#define CONTROL_IROE     0x0008
+#define CONTROL_ITOE     0x0010
+#define CONTROL_ITMT     0x0020
+#define CONTROL_ITRDY    0x0040
+#define CONTROL_IRRDY    0x0080
+#define CONTROL_IE       0x0100
+#define CONTROL_TBRK     0x0200
+#define CONTROL_IDTCS    0x0400
+#define CONTROL_RTS      0x0800
+#define CONTROL_IEOP     0x1000
+
+struct altera_uart {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    CharDriverState *chr;
+    qemu_irq irq;
+
+    uint32_t regs[R_MAX];
+};
+
+static void uart_update_irq(struct altera_uart *s)
+{
+    unsigned int irq;
+
+    irq = (s->regs[R_STATUS] & s->regs[R_CONTROL] &
+          (STATUS_PE | STATUS_FE | STATUS_BRK | STATUS_ROE | STATUS_TOE |
+           STATUS_TMT | STATUS_TRDY | STATUS_RRDY | STATUS_E | STATUS_DTCS));
+    irq = (irq == 0) ? 0 : 1;
+    qemu_set_irq(s->irq, irq);
+}
+
+static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
+                          unsigned int size)
+{
+    struct altera_uart *s = opaque;
+    uint32_t r = 0;
+    addr >>= 2;
+    addr &= 0x7;
+    switch (addr) {
+    case R_RXDATA:
+        r = s->regs[R_RXDATA];
+        s->regs[R_STATUS] &= ~STATUS_RRDY;
+        uart_update_irq(s);
+        break;
+
+    case R_STATUS:
+        r = s->regs[R_STATUS];
+        s->regs[R_STATUS] &= ~(STATUS_PE | STATUS_FE | STATUS_BRK |
+                               STATUS_ROE | STATUS_TOE | STATUS_E |
+                               STATUS_DTCS);
+        uart_update_irq(s);
+        break;
+
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            r = s->regs[addr];
+        }
+        break;
+    }
+
+    return r;
+}
+
+static void uart_write(void *opaque, target_phys_addr_t addr,
+                       uint64_t val64, unsigned int size)
+{
+    struct altera_uart *s = opaque;
+    uint32_t value = val64;
+    unsigned char ch = value;
+
+    addr >>= 2;
+    addr &= 0x7;
+
+    switch (addr) {
+    case R_TXDATA:
+        if (s->chr) {
+            qemu_chr_fe_write(s->chr, &ch, 1);
+        }
+
+        s->regs[addr] = value;
+        break;
+
+    case R_RXDATA:
+    case R_STATUS:
+        /* No writeable bits */
+        break;
+
+    default:
+        s->regs[addr] = value;
+        break;
+    }
+    uart_update_irq(s);
+}
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+    struct altera_uart *s = opaque;
+
+    s->regs[R_RXDATA] = *buf;
+    s->regs[R_STATUS] |= STATUS_RRDY;
+
+    uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+    struct altera_uart *s = opaque;
+    return ((s->regs[R_STATUS] & STATUS_RRDY) == 0);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4
+    }
+};
+
+static int altera_uart_init(SysBusDevice *dev)
+{
+    struct altera_uart *s = FROM_SYSBUS(typeof(*s), dev);
+
+    s->regs[R_STATUS] = STATUS_TMT | STATUS_TRDY; /* Always ready to tx */
+
+    sysbus_init_irq(dev, &s->irq);
+
+    memory_region_init_io(&s->mmio, &uart_ops, s,
+                          "altera,uart", R_MAX * sizeof(uint32_t));
+    sysbus_init_mmio(dev, &s->mmio);
+
+    s->chr = qemu_char_get_next_serial();
+    if (s->chr) {
+        qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+    }
+
+    return 0;
+}
+
+static Property altera_uart_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_uart_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = altera_uart_init;
+    dc->props = altera_uart_properties;
+}
+
+static TypeInfo altera_uart_info = {
+    .name          = "altera,uart",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct altera_uart),
+    .class_init    = altera_uart_class_init,
+};
+
+static void altera_uart_register(void)
+{
+    type_register_static(&altera_uart_info);
+}
+
+type_init(altera_uart_register)
+
diff --git a/hw/altera_vic.c b/hw/altera_vic.c
new file mode 100644
index 0000000..43a2b68
--- /dev/null
+++ b/hw/altera_vic.c
@@ -0,0 +1,195 @@ 
+/*
+ * QEMU Altera Vectored Interrupt Controller.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 "hw.h"
+
+#define R_INT_CONFIG_0      0
+#define R_INT_ENABLE       32
+#define R_INT_ENABLE_SET   33
+#define R_INT_ENABLE_CLR   34
+#define R_INT_PENDING      35
+#define R_INT_RAW_STATUS   36
+#define R_SW_INTERRUPT     37
+#define R_SW_INTERRUPT_SET 38
+#define R_SW_INTERRUPT_CLR 39
+#define R_VIC_CONFIG       40
+#define R_VIC_STATUS       41
+#define R_VEC_TBL_BASE     42
+#define R_VEC_TBL_ADDR     43
+#define R_MAX              44
+
+struct altera_vic {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    qemu_irq parent_irq;
+
+    /* Runtime control registers.  */
+    uint32_t regs[R_MAX];
+};
+
+static void update_irq(struct altera_vic *pv)
+{
+    uint32_t i;
+    pv->regs[R_INT_PENDING] = (pv->regs[R_INT_RAW_STATUS] |
+                               pv->regs[R_SW_INTERRUPT]) &
+                              pv->regs[R_INT_ENABLE];
+
+    for (i = 0; i < 32; i++) {
+        if (pv->regs[R_INT_PENDING] & (1 << i)) {
+            break;
+        }
+    }
+    if (i == 32) {
+        pv->regs[R_VEC_TBL_ADDR] = 0;
+        pv->regs[R_VIC_STATUS] = 0;
+        qemu_irq_lower(pv->parent_irq);
+    } else {
+        pv->regs[R_VEC_TBL_ADDR] = pv->regs[R_VEC_TBL_BASE] +
+                                   i*(4 << (pv->regs[R_VIC_CONFIG] & 7));
+        pv->regs[R_VIC_STATUS] = 0x80000000 | i;
+        qemu_irq_raise(pv->parent_irq);
+    }
+}
+
+static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
+                         unsigned int size)
+{
+    struct altera_vic *pv = opaque;
+    uint32_t r = 0;
+
+    addr >>= 2;
+    if (addr < R_MAX) {
+        r = pv->regs[addr];
+    }
+
+    return r;
+}
+
+static void pic_write(void *opaque, target_phys_addr_t addr,
+                      uint64_t val64, unsigned int size)
+{
+    struct altera_vic *pv = opaque;
+    uint32_t value = val64;
+
+    addr >>= 2;
+    if (addr < R_INT_ENABLE) {
+        /* R_INT_CONFIG_XX */
+        pv->regs[addr] = value & 0x00001FFF;
+    } else {
+        switch (addr) {
+        case R_INT_PENDING:
+        case R_INT_RAW_STATUS:
+        case R_VIC_STATUS:
+        case R_VEC_TBL_ADDR:
+            /* read only */
+            break;
+
+        case R_INT_ENABLE_SET:
+            pv->regs[R_INT_ENABLE] |= value;
+            break;
+
+        case R_SW_INTERRUPT_SET:
+            pv->regs[R_SW_INTERRUPT] |= value;
+            break;
+
+        case R_INT_ENABLE_CLR:
+            pv->regs[R_INT_ENABLE] &= ~value;
+            break;
+
+        case R_SW_INTERRUPT_CLR:
+            pv->regs[R_SW_INTERRUPT] &= ~value;
+            break;
+
+        case R_VIC_CONFIG:
+            pv->regs[addr] = value & 0x0000000F;
+            break;
+
+        default:
+            if (addr < ARRAY_SIZE(pv->regs)) {
+                pv->regs[addr] = value;
+            }
+            break;
+        }
+    }
+    update_irq(pv);
+}
+
+static const MemoryRegionOps pic_ops = {
+    .read = pic_read,
+    .write = pic_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+    struct altera_vic *pv = opaque;
+
+    pv->regs[R_INT_RAW_STATUS] &= ~(1 << irq);
+    pv->regs[R_INT_RAW_STATUS] |= level << irq;
+
+    update_irq(pv);
+}
+
+static int altera_vic_init(SysBusDevice *dev)
+{
+    struct altera_vic *pv = FROM_SYSBUS(typeof(*pv), dev);
+
+    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+    sysbus_init_irq(dev, &pv->parent_irq);
+
+    memset(pv->regs, 0, sizeof(uint32_t) * R_MAX);
+    memory_region_init_io(&pv->mmio, &pic_ops, pv,
+                          "altera,vic", R_MAX * sizeof(uint32_t));
+    sysbus_init_mmio(dev, &pv->mmio);
+    return 0;
+}
+
+static Property altera_vic_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_vic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = altera_vic_init;
+    dc->props = altera_vic_properties;
+}
+
+static TypeInfo altera_vic_info = {
+    .name          = "altera,vic",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct altera_vic),
+    .class_init    = altera_vic_class_init,
+};
+
+static void altera_vic_register(void)
+{
+    type_register_static(&altera_vic_info);
+}
+
+type_init(altera_vic_register)
+
diff --git a/hw/nios2.h b/hw/nios2.h
new file mode 100644
index 0000000..a4af154
--- /dev/null
+++ b/hw/nios2.h
@@ -0,0 +1,46 @@ 
+/*
+ * Altera Nios II CPU interrupt controllers
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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"
+
+qemu_irq *nios2_pic_init_cpu(CPUNios2State *env);
+
+static inline DeviceState *
+altera_vic_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "altera,vic");
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
+
+static inline DeviceState *
+altera_iic_create(qemu_irq irq, int kind_of_intr)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "altera,iic");
+    qdev_init_nofail(dev);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+    return dev;
+}
diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
new file mode 100644
index 0000000..c89b4ae
--- /dev/null
+++ b/hw/nios2_pic_cpu.c
@@ -0,0 +1,48 @@ 
+/*
+ * QEMU Altera Nios II CPU interrupt wrapper logic.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 "hw.h"
+#include "pc.h"
+#include "nios2.h"
+
+void pic_info(Monitor *mon)
+{
+}
+
+void irq_info(Monitor *mon)
+{
+}
+
+static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
+{
+    CPUNios2State *env = (CPUNios2State *)opaque;
+    int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+    if (level) {
+        cpu_interrupt(env, type);
+    } else {
+        cpu_reset_interrupt(env, type);
+    }
+}
+
+qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
+{
+    return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
+}