[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 3/4] Altera: Add support for devices needed to boot
From: |
crwulff |
Subject: |
[Qemu-devel] [PATCH 3/4] Altera: Add support for devices needed to boot Linux on NiosII. (v2) |
Date: |
Sat, 10 Nov 2012 23:18:58 -0500 |
From: Chris Wulff <address@hidden>
Signed-off-by: Chris Wulff <address@hidden>
---
hw/Makefile.objs | 5 ++
hw/altera_timer.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/altera_uart.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/altera_vic.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++
hw/nios2.h | 52 +++++++++++++
hw/nios2_pic_cpu.c | 39 ++++++++++
6 files changed, 714 insertions(+)
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 af4ab0c..32275a5 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -69,6 +69,11 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
common-obj-$(CONFIG_XILINX_AXI) += stream.o
+# Altera devices
+common-obj-$(CONFIG_ALTERA) += altera_vic.o
+common-obj-$(CONFIG_ALTERA) += altera_uart.o
+common-obj-$(CONFIG_ALTERA) += altera_timer.o
+
# PKUnity SoC devices
common-obj-$(CONFIG_PUV3) += puv3_intc.o
common-obj-$(CONFIG_PUV3) += puv3_ost.o
diff --git a/hw/altera_timer.c b/hw/altera_timer.c
new file mode 100644
index 0000000..9c31e59
--- /dev/null
+++ b/hw/altera_timer.c
@@ -0,0 +1,207 @@
+/*
+ * QEMU model of the Altera timer.
+ *
+ * Copyright (c) 2012 Chris Wulff <address@hidden>
+ *
+ * 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
+
+typedef struct AlteraTimer {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq irq;
+ uint32_t freq_hz;
+ QEMUBH *bh;
+ ptimer_state *ptimer;
+ uint32_t regs[R_MAX];
+} AlteraTimer;
+
+static uint64_t timer_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ AlteraTimer *t = opaque;
+ uint64_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;
+ }
+
+ return r;
+}
+
+static void timer_start(AlteraTimer *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 inline int timer_irq_state(AlteraTimer *t)
+{
+ return (t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO) ? 1 : 0;
+}
+
+static void timer_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ AlteraTimer *t = opaque;
+ uint32_t value = val64;
+ uint32_t count = 0;
+ int irqState = timer_irq_state(t);
+
+ 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;
+ }
+
+ if (irqState != timer_irq_state(t)) {
+ qemu_set_irq(t->irq, timer_irq_state(t));
+ }
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static void timer_hit(void *opaque)
+{
+ AlteraTimer *t = opaque;
+
+ t->regs[R_STATUS] |= STATUS_TO;
+
+ if (t->regs[R_CONTROL] & CONTROL_CONT) {
+ timer_start(t);
+ }
+
+ qemu_set_irq(t->irq, timer_irq_state(t));
+}
+
+static int altera_timer_init(SysBusDevice *dev)
+{
+ AlteraTimer *t = FROM_SYSBUS(typeof(*t), dev);
+
+ assert(t->freq_hz != 0);
+
+ 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, "ALTR.timer",
+ R_MAX * sizeof(uint32_t));
+ sysbus_init_mmio(dev, &t->mmio);
+ return 0;
+}
+
+static Property altera_timer_properties[] = {
+ DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, 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 const TypeInfo altera_timer_info = {
+ .name = "ALTR.timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AlteraTimer),
+ .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..862c99e
--- /dev/null
+++ b/hw/altera_uart.c
@@ -0,0 +1,217 @@
+/*
+ * QEMU model of the Altera uart.
+ *
+ * Copyright (c) 2012 Chris Wulff <address@hidden>
+ *
+ * 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
+
+typedef struct AlteraUART {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint32_t regs[R_MAX];
+} AlteraUART;
+
+static void uart_update_irq(AlteraUART *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, hwaddr addr,
+ unsigned int size)
+{
+ AlteraUART *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, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ AlteraUART *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)
+{
+ AlteraUART *s = opaque;
+
+ s->regs[R_RXDATA] = *buf;
+ s->regs[R_STATUS] |= STATUS_RRDY;
+
+ uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ AlteraUART *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_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static int altera_uart_init(SysBusDevice *dev)
+{
+ AlteraUART *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,
+ "ALTR.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 const TypeInfo altera_uart_info = {
+ .name = "ALTR.uart",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AlteraUART),
+ .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..9f6f8b9
--- /dev/null
+++ b/hw/altera_vic.c
@@ -0,0 +1,194 @@
+/*
+ * QEMU Altera Vectored Interrupt Controller.
+ *
+ * Copyright (c) 2012 Chris Wulff <address@hidden>
+ *
+ * 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
+
+typedef struct AlteraVIC {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq parent_irq;
+
+ /* Runtime control registers. */
+ uint32_t regs[R_MAX];
+} AlteraVIC;
+
+static void update_irq(AlteraVIC *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, hwaddr addr,
+ unsigned int size)
+{
+ AlteraVIC *pv = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ if (addr < R_MAX) {
+ r = pv->regs[addr];
+ }
+
+ return r;
+}
+
+static void pic_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ AlteraVIC *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_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ AlteraVIC *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)
+{
+ AlteraVIC *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,
+ "ALTR.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 const TypeInfo altera_vic_info = {
+ .name = "ALTR.vic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AlteraVIC),
+ .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..4f1325a
--- /dev/null
+++ b/hw/nios2.h
@@ -0,0 +1,52 @@
+/*
+ * Altera Nios II CPU interrupt controllers
+ *
+ * Copyright (c) 2012 Chris Wulff <address@hidden>
+ *
+ * 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>
+ */
+
+#ifndef _NIOS2_H_INCLUDED_
+#define _NIOS2_H_INCLUDED_
+
+#include "sysbus.h"
+
+qemu_irq *nios2_pic_init_cpu(Nios2CPU *cpu);
+
+static inline DeviceState *
+altera_vic_create(hwaddr 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(Nios2CPU *cpu, qemu_irq irq, int kind_of_intr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "altera,iic");
+ qdev_prop_set_ptr(dev, "cpu", cpu);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+#endif /* _NIOS2_H_INCLUDED_ */
diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
new file mode 100644
index 0000000..5b6d05b
--- /dev/null
+++ b/hw/nios2_pic_cpu.c
@@ -0,0 +1,39 @@
+/*
+ * QEMU Altera Nios II CPU interrupt wrapper logic.
+ *
+ * Copyright (c) 2012 Chris Wulff <address@hidden>
+ *
+ * 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 "nios2.h"
+
+static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ Nios2CPU *cpu = opaque;
+ int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+ if (level) {
+ cpu_interrupt(&cpu->env, type);
+ } else {
+ cpu_reset_interrupt(&cpu->env, type);
+ }
+}
+
+qemu_irq *nios2_pic_init_cpu(Nios2CPU *cpu)
+{
+ return qemu_allocate_irqs(nios2_pic_cpu_handler, cpu, 2);
+}
--
1.7.10.4