hw: move interrupt controllers to hw/intc/, configure with default-configs/

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2013-02-05 16:12:12 +01:00
parent d2c0bd8458
commit 7702e47c21
35 changed files with 34 additions and 28 deletions

View file

@ -3,3 +3,21 @@ common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
common-obj-$(CONFIG_PL190) += pl190.o
common-obj-$(CONFIG_PUV3) += puv3_intc.o
common-obj-$(CONFIG_XILINX) += xilinx_intc.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_pic.o
common-obj-$(CONFIG_IMX) += imx_avic.o
common-obj-$(CONFIG_LM32) += lm32_pic.o
common-obj-$(CONFIG_REALVIEW) += realview_gic.o
common-obj-$(CONFIG_SLAVIO) += sbi.o slavio_intctl.o sun4c_intctl.o
common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC) += arm_gic.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
obj-$(CONFIG_IOAPIC) += ioapic.o
obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_SH4) += sh_intc.o

911
hw/intc/apic.c Normal file
View file

@ -0,0 +1,911 @@
/*
* APIC support
*
* Copyright (c) 2004-2005 Fabrice Bellard
*
* 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 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/>
*/
#include "qemu/thread.h"
#include "hw/i386/apic_internal.h"
#include "hw/i386/apic.h"
#include "hw/i386/ioapic.h"
#include "hw/pci/msi.h"
#include "qemu/host-utils.h"
#include "trace.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic-msidef.h"
#define MAX_APIC_WORDS 8
#define SYNC_FROM_VAPIC 0x1
#define SYNC_TO_VAPIC 0x2
#define SYNC_ISR_IRR_TO_VAPIC 0x4
static APICCommonState *local_apics[MAX_APICS + 1];
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
static void apic_update_irq(APICCommonState *s);
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
uint8_t dest, uint8_t dest_mode);
/* Find first bit starting from msb */
static int fls_bit(uint32_t value)
{
return 31 - clz32(value);
}
/* Find first bit starting from lsb */
static int ffs_bit(uint32_t value)
{
return ctz32(value);
}
static inline void set_bit(uint32_t *tab, int index)
{
int i, mask;
i = index >> 5;
mask = 1 << (index & 0x1f);
tab[i] |= mask;
}
static inline void reset_bit(uint32_t *tab, int index)
{
int i, mask;
i = index >> 5;
mask = 1 << (index & 0x1f);
tab[i] &= ~mask;
}
static inline int get_bit(uint32_t *tab, int index)
{
int i, mask;
i = index >> 5;
mask = 1 << (index & 0x1f);
return !!(tab[i] & mask);
}
/* return -1 if no bit is set */
static int get_highest_priority_int(uint32_t *tab)
{
int i;
for (i = 7; i >= 0; i--) {
if (tab[i] != 0) {
return i * 32 + fls_bit(tab[i]);
}
}
return -1;
}
static void apic_sync_vapic(APICCommonState *s, int sync_type)
{
VAPICState vapic_state;
size_t length;
off_t start;
int vector;
if (!s->vapic_paddr) {
return;
}
if (sync_type & SYNC_FROM_VAPIC) {
cpu_physical_memory_rw(s->vapic_paddr, (void *)&vapic_state,
sizeof(vapic_state), 0);
s->tpr = vapic_state.tpr;
}
if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) {
start = offsetof(VAPICState, isr);
length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr);
if (sync_type & SYNC_TO_VAPIC) {
assert(qemu_cpu_is_self(CPU(s->cpu)));
vapic_state.tpr = s->tpr;
vapic_state.enabled = 1;
start = 0;
length = sizeof(VAPICState);
}
vector = get_highest_priority_int(s->isr);
if (vector < 0) {
vector = 0;
}
vapic_state.isr = vector & 0xf0;
vapic_state.zero = 0;
vector = get_highest_priority_int(s->irr);
if (vector < 0) {
vector = 0;
}
vapic_state.irr = vector & 0xff;
cpu_physical_memory_write_rom(s->vapic_paddr + start,
((void *)&vapic_state) + start, length);
}
}
static void apic_vapic_base_update(APICCommonState *s)
{
apic_sync_vapic(s, SYNC_TO_VAPIC);
}
static void apic_local_deliver(APICCommonState *s, int vector)
{
uint32_t lvt = s->lvt[vector];
int trigger_mode;
trace_apic_local_deliver(vector, (lvt >> 8) & 7);
if (lvt & APIC_LVT_MASKED)
return;
switch ((lvt >> 8) & 7) {
case APIC_DM_SMI:
cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SMI);
break;
case APIC_DM_NMI:
cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_NMI);
break;
case APIC_DM_EXTINT:
cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
break;
case APIC_DM_FIXED:
trigger_mode = APIC_TRIGGER_EDGE;
if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) &&
(lvt & APIC_LVT_LEVEL_TRIGGER))
trigger_mode = APIC_TRIGGER_LEVEL;
apic_set_irq(s, lvt & 0xff, trigger_mode);
}
}
void apic_deliver_pic_intr(DeviceState *d, int level)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
if (level) {
apic_local_deliver(s, APIC_LVT_LINT0);
} else {
uint32_t lvt = s->lvt[APIC_LVT_LINT0];
switch ((lvt >> 8) & 7) {
case APIC_DM_FIXED:
if (!(lvt & APIC_LVT_LEVEL_TRIGGER))
break;
reset_bit(s->irr, lvt & 0xff);
/* fall through */
case APIC_DM_EXTINT:
cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
break;
}
}
}
static void apic_external_nmi(APICCommonState *s)
{
apic_local_deliver(s, APIC_LVT_LINT1);
}
#define foreach_apic(apic, deliver_bitmask, code) \
{\
int __i, __j, __mask;\
for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
__mask = deliver_bitmask[__i];\
if (__mask) {\
for(__j = 0; __j < 32; __j++) {\
if (__mask & (1 << __j)) {\
apic = local_apics[__i * 32 + __j];\
if (apic) {\
code;\
}\
}\
}\
}\
}\
}
static void apic_bus_deliver(const uint32_t *deliver_bitmask,
uint8_t delivery_mode, uint8_t vector_num,
uint8_t trigger_mode)
{
APICCommonState *apic_iter;
switch (delivery_mode) {
case APIC_DM_LOWPRI:
/* XXX: search for focus processor, arbitration */
{
int i, d;
d = -1;
for(i = 0; i < MAX_APIC_WORDS; i++) {
if (deliver_bitmask[i]) {
d = i * 32 + ffs_bit(deliver_bitmask[i]);
break;
}
}
if (d >= 0) {
apic_iter = local_apics[d];
if (apic_iter) {
apic_set_irq(apic_iter, vector_num, trigger_mode);
}
}
}
return;
case APIC_DM_FIXED:
break;
case APIC_DM_SMI:
foreach_apic(apic_iter, deliver_bitmask,
cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_SMI)
);
return;
case APIC_DM_NMI:
foreach_apic(apic_iter, deliver_bitmask,
cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_NMI)
);
return;
case APIC_DM_INIT:
/* normal INIT IPI sent to processors */
foreach_apic(apic_iter, deliver_bitmask,
cpu_interrupt(CPU(apic_iter->cpu),
CPU_INTERRUPT_INIT)
);
return;
case APIC_DM_EXTINT:
/* handled in I/O APIC code */
break;
default:
return;
}
foreach_apic(apic_iter, deliver_bitmask,
apic_set_irq(apic_iter, vector_num, trigger_mode) );
}
void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode,
uint8_t vector_num, uint8_t trigger_mode)
{
uint32_t deliver_bitmask[MAX_APIC_WORDS];
trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num,
trigger_mode);
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode);
}
static void apic_set_base(APICCommonState *s, uint64_t val)
{
s->apicbase = (val & 0xfffff000) |
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
/* if disabled, cannot be enabled again */
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
cpu_clear_apic_feature(&s->cpu->env);
s->spurious_vec &= ~APIC_SV_ENABLE;
}
}
static void apic_set_tpr(APICCommonState *s, uint8_t val)
{
/* Updates from cr8 are ignored while the VAPIC is active */
if (!s->vapic_paddr) {
s->tpr = val << 4;
apic_update_irq(s);
}
}
static uint8_t apic_get_tpr(APICCommonState *s)
{
apic_sync_vapic(s, SYNC_FROM_VAPIC);
return s->tpr >> 4;
}
static int apic_get_ppr(APICCommonState *s)
{
int tpr, isrv, ppr;
tpr = (s->tpr >> 4);
isrv = get_highest_priority_int(s->isr);
if (isrv < 0)
isrv = 0;
isrv >>= 4;
if (tpr >= isrv)
ppr = s->tpr;
else
ppr = isrv << 4;
return ppr;
}
static int apic_get_arb_pri(APICCommonState *s)
{
/* XXX: arbitration */
return 0;
}
/*
* <0 - low prio interrupt,
* 0 - no interrupt,
* >0 - interrupt number
*/
static int apic_irq_pending(APICCommonState *s)
{
int irrv, ppr;
irrv = get_highest_priority_int(s->irr);
if (irrv < 0) {
return 0;
}
ppr = apic_get_ppr(s);
if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) {
return -1;
}
return irrv;
}
/* signal the CPU if an irq is pending */
static void apic_update_irq(APICCommonState *s)
{
CPUState *cpu;
if (!(s->spurious_vec & APIC_SV_ENABLE)) {
return;
}
cpu = CPU(s->cpu);
if (!qemu_cpu_is_self(cpu)) {
cpu_interrupt(cpu, CPU_INTERRUPT_POLL);
} else if (apic_irq_pending(s) > 0) {
cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
}
}
void apic_poll_irq(DeviceState *d)
{
APICCommonState *s = APIC_COMMON(d);
apic_sync_vapic(s, SYNC_FROM_VAPIC);
apic_update_irq(s);
}
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode)
{
apic_report_irq_delivered(!get_bit(s->irr, vector_num));
set_bit(s->irr, vector_num);
if (trigger_mode)
set_bit(s->tmr, vector_num);
else
reset_bit(s->tmr, vector_num);
if (s->vapic_paddr) {
apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC);
/*
* The vcpu thread needs to see the new IRR before we pull its current
* TPR value. That way, if we miss a lowering of the TRP, the guest
* has the chance to notice the new IRR and poll for IRQs on its own.
*/
smp_wmb();
apic_sync_vapic(s, SYNC_FROM_VAPIC);
}
apic_update_irq(s);
}
static void apic_eoi(APICCommonState *s)
{
int isrv;
isrv = get_highest_priority_int(s->isr);
if (isrv < 0)
return;
reset_bit(s->isr, isrv);
if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) {
ioapic_eoi_broadcast(isrv);
}
apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC);
apic_update_irq(s);
}
static int apic_find_dest(uint8_t dest)
{
APICCommonState *apic = local_apics[dest];
int i;
if (apic && apic->id == dest)
return dest; /* shortcut in case apic->id == apic->idx */
for (i = 0; i < MAX_APICS; i++) {
apic = local_apics[i];
if (apic && apic->id == dest)
return i;
if (!apic)
break;
}
return -1;
}
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
uint8_t dest, uint8_t dest_mode)
{
APICCommonState *apic_iter;
int i;
if (dest_mode == 0) {
if (dest == 0xff) {
memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
} else {
int idx = apic_find_dest(dest);
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
if (idx >= 0)
set_bit(deliver_bitmask, idx);
}
} else {
/* XXX: cluster mode */
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
for(i = 0; i < MAX_APICS; i++) {
apic_iter = local_apics[i];
if (apic_iter) {
if (apic_iter->dest_mode == 0xf) {
if (dest & apic_iter->log_dest)
set_bit(deliver_bitmask, i);
} else if (apic_iter->dest_mode == 0x0) {
if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
(dest & apic_iter->log_dest & 0x0f)) {
set_bit(deliver_bitmask, i);
}
}
} else {
break;
}
}
}
}
static void apic_startup(APICCommonState *s, int vector_num)
{
s->sipi_vector = vector_num;
cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI);
}
void apic_sipi(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI);
if (!s->wait_for_sipi)
return;
cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector);
s->wait_for_sipi = 0;
}
static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode,
uint8_t delivery_mode, uint8_t vector_num,
uint8_t trigger_mode)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
uint32_t deliver_bitmask[MAX_APIC_WORDS];
int dest_shorthand = (s->icr[0] >> 18) & 3;
APICCommonState *apic_iter;
switch (dest_shorthand) {
case 0:
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
break;
case 1:
memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
set_bit(deliver_bitmask, s->idx);
break;
case 2:
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
break;
case 3:
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
reset_bit(deliver_bitmask, s->idx);
break;
}
switch (delivery_mode) {
case APIC_DM_INIT:
{
int trig_mode = (s->icr[0] >> 15) & 1;
int level = (s->icr[0] >> 14) & 1;
if (level == 0 && trig_mode == 1) {
foreach_apic(apic_iter, deliver_bitmask,
apic_iter->arb_id = apic_iter->id );
return;
}
}
break;
case APIC_DM_SIPI:
foreach_apic(apic_iter, deliver_bitmask,
apic_startup(apic_iter, vector_num) );
return;
}
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode);
}
static bool apic_check_pic(APICCommonState *s)
{
if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
return false;
}
apic_deliver_pic_intr(&s->busdev.qdev, 1);
return true;
}
int apic_get_interrupt(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
int intno;
/* if the APIC is installed or enabled, we let the 8259 handle the
IRQs */
if (!s)
return -1;
if (!(s->spurious_vec & APIC_SV_ENABLE))
return -1;
apic_sync_vapic(s, SYNC_FROM_VAPIC);
intno = apic_irq_pending(s);
if (intno == 0) {
apic_sync_vapic(s, SYNC_TO_VAPIC);
return -1;
} else if (intno < 0) {
apic_sync_vapic(s, SYNC_TO_VAPIC);
return s->spurious_vec & 0xff;
}
reset_bit(s->irr, intno);
set_bit(s->isr, intno);
apic_sync_vapic(s, SYNC_TO_VAPIC);
/* re-inject if there is still a pending PIC interrupt */
apic_check_pic(s);
apic_update_irq(s);
return intno;
}
int apic_accept_pic_intr(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
uint32_t lvt0;
if (!s)
return -1;
lvt0 = s->lvt[APIC_LVT_LINT0];
if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 ||
(lvt0 & APIC_LVT_MASKED) == 0)
return 1;
return 0;
}
static uint32_t apic_get_current_count(APICCommonState *s)
{
int64_t d;
uint32_t val;
d = (qemu_get_clock_ns(vm_clock) - s->initial_count_load_time) >>
s->count_shift;
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
/* periodic */
val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
} else {
if (d >= s->initial_count)
val = 0;
else
val = s->initial_count - d;
}
return val;
}
static void apic_timer_update(APICCommonState *s, int64_t current_time)
{
if (apic_next_timer(s, current_time)) {
qemu_mod_timer(s->timer, s->next_time);
} else {
qemu_del_timer(s->timer);
}
}
static void apic_timer(void *opaque)
{
APICCommonState *s = opaque;
apic_local_deliver(s, APIC_LVT_TIMER);
apic_timer_update(s, s->next_time);
}
static uint32_t apic_mem_readb(void *opaque, hwaddr addr)
{
return 0;
}
static uint32_t apic_mem_readw(void *opaque, hwaddr addr)
{
return 0;
}
static void apic_mem_writeb(void *opaque, hwaddr addr, uint32_t val)
{
}
static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val)
{
}
static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
{
DeviceState *d;
APICCommonState *s;
uint32_t val;
int index;
d = cpu_get_current_apic();
if (!d) {
return 0;
}
s = DO_UPCAST(APICCommonState, busdev.qdev, d);
index = (addr >> 4) & 0xff;
switch(index) {
case 0x02: /* id */
val = s->id << 24;
break;
case 0x03: /* version */
val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
break;
case 0x08:
apic_sync_vapic(s, SYNC_FROM_VAPIC);
if (apic_report_tpr_access) {
cpu_report_tpr_access(&s->cpu->env, TPR_ACCESS_READ);
}
val = s->tpr;
break;
case 0x09:
val = apic_get_arb_pri(s);
break;
case 0x0a:
/* ppr */
val = apic_get_ppr(s);
break;
case 0x0b:
val = 0;
break;
case 0x0d:
val = s->log_dest << 24;
break;
case 0x0e:
val = s->dest_mode << 28;
break;
case 0x0f:
val = s->spurious_vec;
break;
case 0x10 ... 0x17:
val = s->isr[index & 7];
break;
case 0x18 ... 0x1f:
val = s->tmr[index & 7];
break;
case 0x20 ... 0x27:
val = s->irr[index & 7];
break;
case 0x28:
val = s->esr;
break;
case 0x30:
case 0x31:
val = s->icr[index & 1];
break;
case 0x32 ... 0x37:
val = s->lvt[index - 0x32];
break;
case 0x38:
val = s->initial_count;
break;
case 0x39:
val = apic_get_current_count(s);
break;
case 0x3e:
val = s->divide_conf;
break;
default:
s->esr |= ESR_ILLEGAL_ADDRESS;
val = 0;
break;
}
trace_apic_mem_readl(addr, val);
return val;
}
static void apic_send_msi(hwaddr addr, uint32_t data)
{
uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
/* XXX: Ignore redirection hint. */
apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode);
}
static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
{
DeviceState *d;
APICCommonState *s;
int index = (addr >> 4) & 0xff;
if (addr > 0xfff || !index) {
/* MSI and MMIO APIC are at the same memory location,
* but actually not on the global bus: MSI is on PCI bus
* APIC is connected directly to the CPU.
* Mapping them on the global bus happens to work because
* MSI registers are reserved in APIC MMIO and vice versa. */
apic_send_msi(addr, val);
return;
}
d = cpu_get_current_apic();
if (!d) {
return;
}
s = DO_UPCAST(APICCommonState, busdev.qdev, d);
trace_apic_mem_writel(addr, val);
switch(index) {
case 0x02:
s->id = (val >> 24);
break;
case 0x03:
break;
case 0x08:
if (apic_report_tpr_access) {
cpu_report_tpr_access(&s->cpu->env, TPR_ACCESS_WRITE);
}
s->tpr = val;
apic_sync_vapic(s, SYNC_TO_VAPIC);
apic_update_irq(s);
break;
case 0x09:
case 0x0a:
break;
case 0x0b: /* EOI */
apic_eoi(s);
break;
case 0x0d:
s->log_dest = val >> 24;
break;
case 0x0e:
s->dest_mode = val >> 28;
break;
case 0x0f:
s->spurious_vec = val & 0x1ff;
apic_update_irq(s);
break;
case 0x10 ... 0x17:
case 0x18 ... 0x1f:
case 0x20 ... 0x27:
case 0x28:
break;
case 0x30:
s->icr[0] = val;
apic_deliver(d, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
(s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
(s->icr[0] >> 15) & 1);
break;
case 0x31:
s->icr[1] = val;
break;
case 0x32 ... 0x37:
{
int n = index - 0x32;
s->lvt[n] = val;
if (n == APIC_LVT_TIMER) {
apic_timer_update(s, qemu_get_clock_ns(vm_clock));
} else if (n == APIC_LVT_LINT0 && apic_check_pic(s)) {
apic_update_irq(s);
}
}
break;
case 0x38:
s->initial_count = val;
s->initial_count_load_time = qemu_get_clock_ns(vm_clock);
apic_timer_update(s, s->initial_count_load_time);
break;
case 0x39:
break;
case 0x3e:
{
int v;
s->divide_conf = val & 0xb;
v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
s->count_shift = (v + 1) & 7;
}
break;
default:
s->esr |= ESR_ILLEGAL_ADDRESS;
break;
}
}
static void apic_pre_save(APICCommonState *s)
{
apic_sync_vapic(s, SYNC_FROM_VAPIC);
}
static void apic_post_load(APICCommonState *s)
{
if (s->timer_expiry != -1) {
qemu_mod_timer(s->timer, s->timer_expiry);
} else {
qemu_del_timer(s->timer);
}
}
static const MemoryRegionOps apic_io_ops = {
.old_mmio = {
.read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, },
.write = { apic_mem_writeb, apic_mem_writew, apic_mem_writel, },
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void apic_init(APICCommonState *s)
{
memory_region_init_io(&s->io_memory, &apic_io_ops, s, "apic-msi",
MSI_SPACE_SIZE);
s->timer = qemu_new_timer_ns(vm_clock, apic_timer, s);
local_apics[s->idx] = s;
msi_supported = true;
}
static void apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
k->init = apic_init;
k->set_base = apic_set_base;
k->set_tpr = apic_set_tpr;
k->get_tpr = apic_get_tpr;
k->vapic_base_update = apic_vapic_base_update;
k->external_nmi = apic_external_nmi;
k->pre_save = apic_pre_save;
k->post_load = apic_post_load;
}
static const TypeInfo apic_info = {
.name = "apic",
.instance_size = sizeof(APICCommonState),
.parent = TYPE_APIC_COMMON,
.class_init = apic_class_init,
};
static void apic_register_types(void)
{
type_register_static(&apic_info);
}
type_init(apic_register_types)

402
hw/intc/apic_common.c Normal file
View file

@ -0,0 +1,402 @@
/*
* APIC support - common bits of emulated and KVM kernel model
*
* Copyright (c) 2004-2005 Fabrice Bellard
* Copyright (c) 2011 Jan Kiszka, Siemens AG
*
* 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 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/>
*/
#include "hw/i386/apic.h"
#include "hw/i386/apic_internal.h"
#include "trace.h"
#include "sysemu/kvm.h"
static int apic_irq_delivered;
bool apic_report_tpr_access;
void cpu_set_apic_base(DeviceState *d, uint64_t val)
{
trace_cpu_set_apic_base(val);
if (d) {
APICCommonState *s = APIC_COMMON(d);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
info->set_base(s, val);
}
}
uint64_t cpu_get_apic_base(DeviceState *d)
{
if (d) {
APICCommonState *s = APIC_COMMON(d);
trace_cpu_get_apic_base((uint64_t)s->apicbase);
return s->apicbase;
} else {
trace_cpu_get_apic_base(MSR_IA32_APICBASE_BSP);
return MSR_IA32_APICBASE_BSP;
}
}
void cpu_set_apic_tpr(DeviceState *d, uint8_t val)
{
APICCommonState *s;
APICCommonClass *info;
if (!d) {
return;
}
s = APIC_COMMON(d);
info = APIC_COMMON_GET_CLASS(s);
info->set_tpr(s, val);
}
uint8_t cpu_get_apic_tpr(DeviceState *d)
{
APICCommonState *s;
APICCommonClass *info;
if (!d) {
return 0;
}
s = APIC_COMMON(d);
info = APIC_COMMON_GET_CLASS(s);
return info->get_tpr(s);
}
void apic_enable_tpr_access_reporting(DeviceState *d, bool enable)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
apic_report_tpr_access = enable;
if (info->enable_tpr_reporting) {
info->enable_tpr_reporting(s, enable);
}
}
void apic_enable_vapic(DeviceState *d, hwaddr paddr)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
s->vapic_paddr = paddr;
info->vapic_base_update(s);
}
void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
TPRAccess access)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access);
}
void apic_report_irq_delivered(int delivered)
{
apic_irq_delivered += delivered;
trace_apic_report_irq_delivered(apic_irq_delivered);
}
void apic_reset_irq_delivered(void)
{
trace_apic_reset_irq_delivered(apic_irq_delivered);
apic_irq_delivered = 0;
}
int apic_get_irq_delivered(void)
{
trace_apic_get_irq_delivered(apic_irq_delivered);
return apic_irq_delivered;
}
void apic_deliver_nmi(DeviceState *d)
{
APICCommonState *s = APIC_COMMON(d);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
info->external_nmi(s);
}
bool apic_next_timer(APICCommonState *s, int64_t current_time)
{
int64_t d;
/* We need to store the timer state separately to support APIC
* implementations that maintain a non-QEMU timer, e.g. inside the
* host kernel. This open-coded state allows us to migrate between
* both models. */
s->timer_expiry = -1;
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) {
return false;
}
d = (current_time - s->initial_count_load_time) >> s->count_shift;
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
if (!s->initial_count) {
return false;
}
d = ((d / ((uint64_t)s->initial_count + 1)) + 1) *
((uint64_t)s->initial_count + 1);
} else {
if (d >= s->initial_count) {
return false;
}
d = (uint64_t)s->initial_count + 1;
}
s->next_time = s->initial_count_load_time + (d << s->count_shift);
s->timer_expiry = s->next_time;
return true;
}
void apic_init_reset(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
int i;
if (!s) {
return;
}
s->tpr = 0;
s->spurious_vec = 0xff;
s->log_dest = 0;
s->dest_mode = 0xf;
memset(s->isr, 0, sizeof(s->isr));
memset(s->tmr, 0, sizeof(s->tmr));
memset(s->irr, 0, sizeof(s->irr));
for (i = 0; i < APIC_LVT_NB; i++) {
s->lvt[i] = APIC_LVT_MASKED;
}
s->esr = 0;
memset(s->icr, 0, sizeof(s->icr));
s->divide_conf = 0;
s->count_shift = 0;
s->initial_count = 0;
s->initial_count_load_time = 0;
s->next_time = 0;
s->wait_for_sipi = 1;
if (s->timer) {
qemu_del_timer(s->timer);
}
s->timer_expiry = -1;
}
void apic_designate_bsp(DeviceState *d)
{
if (d == NULL) {
return;
}
APICCommonState *s = APIC_COMMON(d);
s->apicbase |= MSR_IA32_APICBASE_BSP;
}
static void apic_reset_common(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
bool bsp;
bsp = cpu_is_bsp(s->cpu);
s->apicbase = APIC_DEFAULT_ADDRESS |
(bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
s->vapic_paddr = 0;
info->vapic_base_update(s);
apic_init_reset(d);
if (bsp) {
/*
* LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
* time typically by BIOS, so PIC interrupt can be delivered to the
* processor when local APIC is enabled.
*/
s->lvt[APIC_LVT_LINT0] = 0x700;
}
}
/* This function is only used for old state version 1 and 2 */
static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
{
APICCommonState *s = opaque;
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
int i;
if (version_id > 2) {
return -EINVAL;
}
/* XXX: what if the base changes? (registered memory regions) */
qemu_get_be32s(f, &s->apicbase);
qemu_get_8s(f, &s->id);
qemu_get_8s(f, &s->arb_id);
qemu_get_8s(f, &s->tpr);
qemu_get_be32s(f, &s->spurious_vec);
qemu_get_8s(f, &s->log_dest);
qemu_get_8s(f, &s->dest_mode);
for (i = 0; i < 8; i++) {
qemu_get_be32s(f, &s->isr[i]);
qemu_get_be32s(f, &s->tmr[i]);
qemu_get_be32s(f, &s->irr[i]);
}
for (i = 0; i < APIC_LVT_NB; i++) {
qemu_get_be32s(f, &s->lvt[i]);
}
qemu_get_be32s(f, &s->esr);
qemu_get_be32s(f, &s->icr[0]);
qemu_get_be32s(f, &s->icr[1]);
qemu_get_be32s(f, &s->divide_conf);
s->count_shift = qemu_get_be32(f);
qemu_get_be32s(f, &s->initial_count);
s->initial_count_load_time = qemu_get_be64(f);
s->next_time = qemu_get_be64(f);
if (version_id >= 2) {
s->timer_expiry = qemu_get_be64(f);
}
if (info->post_load) {
info->post_load(s);
}
return 0;
}
static int apic_init_common(SysBusDevice *dev)
{
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info;
static DeviceState *vapic;
static int apic_no;
if (apic_no >= MAX_APICS) {
return -1;
}
s->idx = apic_no++;
info = APIC_COMMON_GET_CLASS(s);
info->init(s);
sysbus_init_mmio(dev, &s->io_memory);
/* Note: We need at least 1M to map the VAPIC option ROM */
if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK &&
ram_size >= 1024 * 1024) {
vapic = sysbus_create_simple("kvmvapic", -1, NULL);
}
s->vapic = vapic;
if (apic_report_tpr_access && info->enable_tpr_reporting) {
info->enable_tpr_reporting(s, true);
}
return 0;
}
static void apic_dispatch_pre_save(void *opaque)
{
APICCommonState *s = APIC_COMMON(opaque);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
if (info->pre_save) {
info->pre_save(s);
}
}
static int apic_dispatch_post_load(void *opaque, int version_id)
{
APICCommonState *s = APIC_COMMON(opaque);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
if (info->post_load) {
info->post_load(s);
}
return 0;
}
static const VMStateDescription vmstate_apic_common = {
.name = "apic",
.version_id = 3,
.minimum_version_id = 3,
.minimum_version_id_old = 1,
.load_state_old = apic_load_old,
.pre_save = apic_dispatch_pre_save,
.post_load = apic_dispatch_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(apicbase, APICCommonState),
VMSTATE_UINT8(id, APICCommonState),
VMSTATE_UINT8(arb_id, APICCommonState),
VMSTATE_UINT8(tpr, APICCommonState),
VMSTATE_UINT32(spurious_vec, APICCommonState),
VMSTATE_UINT8(log_dest, APICCommonState),
VMSTATE_UINT8(dest_mode, APICCommonState),
VMSTATE_UINT32_ARRAY(isr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(irr, APICCommonState, 8),
VMSTATE_UINT32_ARRAY(lvt, APICCommonState, APIC_LVT_NB),
VMSTATE_UINT32(esr, APICCommonState),
VMSTATE_UINT32_ARRAY(icr, APICCommonState, 2),
VMSTATE_UINT32(divide_conf, APICCommonState),
VMSTATE_INT32(count_shift, APICCommonState),
VMSTATE_UINT32(initial_count, APICCommonState),
VMSTATE_INT64(initial_count_load_time, APICCommonState),
VMSTATE_INT64(next_time, APICCommonState),
VMSTATE_INT64(timer_expiry,
APICCommonState), /* open-coded timer state */
VMSTATE_END_OF_LIST()
}
};
static Property apic_properties_common[] = {
DEFINE_PROP_UINT8("id", APICCommonState, id, -1),
DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT,
true),
DEFINE_PROP_END_OF_LIST(),
};
static void apic_common_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
dc->no_user = 1;
dc->props = apic_properties_common;
sc->init = apic_init_common;
}
static const TypeInfo apic_common_type = {
.name = TYPE_APIC_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(APICCommonState),
.class_size = sizeof(APICCommonClass),
.class_init = apic_common_class_init,
.abstract = true,
};
static void register_types(void)
{
type_register_static(&apic_common_type);
}
type_init(register_types)

723
hw/intc/arm_gic.c Normal file
View file

@ -0,0 +1,723 @@
/*
* ARM Generic/Distributed Interrupt Controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*/
/* This file contains implementation code for the RealView EB interrupt
* controller, MPCore distributed interrupt controller and ARMv7-M
* Nested Vectored Interrupt Controller.
* It is compiled in two ways:
* (1) as a standalone file to produce a sysbus device which is a GIC
* that can be used on the realview board and as one of the builtin
* private peripherals for the ARM MP CPUs (11MPCore, A9, etc)
* (2) by being directly #included into armv7m_nvic.c to produce the
* armv7m_nvic device.
*/
#include "hw/sysbus.h"
#include "hw/arm_gic_internal.h"
//#define DEBUG_GIC
#ifdef DEBUG_GIC
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#endif
static const uint8_t gic_id[] = {
0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
};
#define NUM_CPU(s) ((s)->num_cpu)
static inline int gic_get_current_cpu(GICState *s)
{
if (s->num_cpu > 1) {
CPUState *cpu = ENV_GET_CPU(cpu_single_env);
return cpu->cpu_index;
}
return 0;
}
/* TODO: Many places that call this routine could be optimized. */
/* Update interrupt status after enabled or pending bits have been changed. */
void gic_update(GICState *s)
{
int best_irq;
int best_prio;
int irq;
int level;
int cpu;
int cm;
for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
if (!s->enabled || !s->cpu_enabled[cpu]) {
qemu_irq_lower(s->parent_irq[cpu]);
return;
}
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < s->num_irq; irq++) {
if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
best_prio = GIC_GET_PRIORITY(irq, cpu);
best_irq = irq;
}
}
}
level = 0;
if (best_prio < s->priority_mask[cpu]) {
s->current_pending[cpu] = best_irq;
if (best_prio < s->running_priority[cpu]) {
DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu);
level = 1;
}
}
qemu_set_irq(s->parent_irq[cpu], level);
}
}
void gic_set_pending_private(GICState *s, int cpu, int irq)
{
int cm = 1 << cpu;
if (GIC_TEST_PENDING(irq, cm))
return;
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
GIC_SET_PENDING(irq, cm);
gic_update(s);
}
/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
* [N..N+31] : PPI (internal) interrupts for CPU 0
* [N+32..N+63] : PPI (internal interrupts for CPU 1
* ...
*/
GICState *s = (GICState *)opaque;
int cm, target;
if (irq < (s->num_irq - GIC_INTERNAL)) {
/* The first external input line is internal interrupt 32. */
cm = ALL_CPU_MASK;
irq += GIC_INTERNAL;
target = GIC_TARGET(irq);
} else {
int cpu;
irq -= (s->num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
cm = 1 << cpu;
target = cm;
}
if (level == GIC_TEST_LEVEL(irq, cm)) {
return;
}
if (level) {
GIC_SET_LEVEL(irq, cm);
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
DPRINTF("Set %d pending mask %x\n", irq, target);
GIC_SET_PENDING(irq, target);
}
} else {
GIC_CLEAR_LEVEL(irq, cm);
}
gic_update(s);
}
static void gic_set_running_irq(GICState *s, int cpu, int irq)
{
s->running_irq[cpu] = irq;
if (irq == 1023) {
s->running_priority[cpu] = 0x100;
} else {
s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
}
gic_update(s);
}
uint32_t gic_acknowledge_irq(GICState *s, int cpu)
{
int new_irq;
int cm = 1 << cpu;
new_irq = s->current_pending[cpu];
if (new_irq == 1023
|| GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
DPRINTF("ACK no pending IRQ\n");
return 1023;
}
s->last_active[new_irq][cpu] = s->running_irq[cpu];
/* Clear pending flags for both level and edge triggered interrupts.
Level triggered IRQs will be reasserted once they become inactive. */
GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
gic_set_running_irq(s, cpu, new_irq);
DPRINTF("ACK %d\n", new_irq);
return new_irq;
}
void gic_complete_irq(GICState *s, int cpu, int irq)
{
int update = 0;
int cm = 1 << cpu;
DPRINTF("EOI %d\n", irq);
if (irq >= s->num_irq) {
/* This handles two cases:
* 1. If software writes the ID of a spurious interrupt [ie 1023]
* to the GICC_EOIR, the GIC ignores that write.
* 2. If software writes the number of a non-existent interrupt
* this must be a subcase of "value written does not match the last
* valid interrupt value read from the Interrupt Acknowledge
* register" and so this is UNPREDICTABLE. We choose to ignore it.
*/
return;
}
if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
/* Mark level triggered interrupts as pending if they are still
raised. */
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
update = 1;
}
if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
int tmp = s->running_irq[cpu];
while (s->last_active[tmp][cpu] != 1023) {
if (s->last_active[tmp][cpu] == irq) {
s->last_active[tmp][cpu] = s->last_active[irq][cpu];
break;
}
tmp = s->last_active[tmp][cpu];
}
if (update) {
gic_update(s);
}
} else {
/* Complete the current running IRQ. */
gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
}
}
static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
{
GICState *s = (GICState *)opaque;
uint32_t res;
int irq;
int i;
int cpu;
int cm;
int mask;
cpu = gic_get_current_cpu(s);
cm = 1 << cpu;
if (offset < 0x100) {
if (offset == 0)
return s->enabled;
if (offset == 4)
return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5);
if (offset < 0x08)
return 0;
if (offset >= 0x80) {
/* Interrupt Security , RAZ/WI */
return 0;
}
goto bad_reg;
} else if (offset < 0x200) {
/* Interrupt Set/Clear Enable. */
if (offset < 0x180)
irq = (offset - 0x100) * 8;
else
irq = (offset - 0x180) * 8;
irq += GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
res = 0;
for (i = 0; i < 8; i++) {
if (GIC_TEST_ENABLED(irq + i, cm)) {
res |= (1 << i);
}
}
} else if (offset < 0x300) {
/* Interrupt Set/Clear Pending. */
if (offset < 0x280)
irq = (offset - 0x200) * 8;
else
irq = (offset - 0x280) * 8;
irq += GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
res = 0;
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
if (GIC_TEST_PENDING(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x400) {
/* Interrupt Active. */
irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
res = 0;
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
if (GIC_TEST_ACTIVE(irq + i, mask)) {
res |= (1 << i);
}
}
} else if (offset < 0x800) {
/* Interrupt Priority. */
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
res = GIC_GET_PRIORITY(irq, cpu);
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
if (s->num_cpu == 1 && s->revision != REV_11MPCORE) {
/* For uniprocessor GICs these RAZ/WI */
res = 0;
} else {
irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= s->num_irq) {
goto bad_reg;
}
if (irq >= 29 && irq <= 31) {
res = cm;
} else {
res = GIC_TARGET(irq);
}
}
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
res = 0;
for (i = 0; i < 4; i++) {
if (GIC_TEST_MODEL(irq + i))
res |= (1 << (i * 2));
if (GIC_TEST_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
} else if (offset < 0xfe0) {
goto bad_reg;
} else /* offset >= 0xfe0 */ {
if (offset & 3) {
res = 0;
} else {
res = gic_id[(offset - 0xfe0) >> 2];
}
}
return res;
bad_reg:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_dist_readb: Bad offset %x\n", (int)offset);
return 0;
}
static uint32_t gic_dist_readw(void *opaque, hwaddr offset)
{
uint32_t val;
val = gic_dist_readb(opaque, offset);
val |= gic_dist_readb(opaque, offset + 1) << 8;
return val;
}
static uint32_t gic_dist_readl(void *opaque, hwaddr offset)
{
uint32_t val;
val = gic_dist_readw(opaque, offset);
val |= gic_dist_readw(opaque, offset + 2) << 16;
return val;
}
static void gic_dist_writeb(void *opaque, hwaddr offset,
uint32_t value)
{
GICState *s = (GICState *)opaque;
int irq;
int i;
int cpu;
cpu = gic_get_current_cpu(s);
if (offset < 0x100) {
if (offset == 0) {
s->enabled = (value & 1);
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
} else if (offset < 4) {
/* ignored. */
} else if (offset >= 0x80) {
/* Interrupt Security Registers, RAZ/WI */
} else {
goto bad_reg;
}
} else if (offset < 0x180) {
/* Interrupt Set Enable. */
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
if (irq < 16)
value = 0xff;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int mask =
(irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq + i);
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
if (!GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Enabled IRQ %d\n", irq + i);
}
GIC_SET_ENABLED(irq + i, cm);
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i, mask)
&& !GIC_TEST_TRIGGER(irq + i)) {
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
GIC_SET_PENDING(irq + i, mask);
}
}
}
} else if (offset < 0x200) {
/* Interrupt Clear Enable. */
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
if (irq < 16)
value = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
if (GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Disabled IRQ %d\n", irq + i);
}
GIC_CLEAR_ENABLED(irq + i, cm);
}
}
} else if (offset < 0x280) {
/* Interrupt Set Pending. */
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
if (irq < 16)
irq = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i));
}
}
} else if (offset < 0x300) {
/* Interrupt Clear Pending. */
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
for (i = 0; i < 8; i++) {
/* ??? This currently clears the pending bit for all CPUs, even
for per-CPU interrupts. It's unclear whether this is the
corect behavior. */
if (value & (1 << i)) {
GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
}
}
} else if (offset < 0x400) {
/* Interrupt Active. */
goto bad_reg;
} else if (offset < 0x800) {
/* Interrupt Priority. */
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
if (irq < GIC_INTERNAL) {
s->priority1[irq][cpu] = value;
} else {
s->priority2[irq - GIC_INTERNAL] = value;
}
} else if (offset < 0xc00) {
/* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
* annoying exception of the 11MPCore's GIC.
*/
if (s->num_cpu != 1 || s->revision == REV_11MPCORE) {
irq = (offset - 0x800) + GIC_BASE_IRQ;
if (irq >= s->num_irq) {
goto bad_reg;
}
if (irq < 29) {
value = 0;
} else if (irq < GIC_INTERNAL) {
value = ALL_CPU_MASK;
}
s->irq_target[irq] = value & ALL_CPU_MASK;
}
} else if (offset < 0xf00) {
/* Interrupt Configuration. */
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
if (irq < GIC_INTERNAL)
value |= 0xaa;
for (i = 0; i < 4; i++) {
if (value & (1 << (i * 2))) {
GIC_SET_MODEL(irq + i);
} else {
GIC_CLEAR_MODEL(irq + i);
}
if (value & (2 << (i * 2))) {
GIC_SET_TRIGGER(irq + i);
} else {
GIC_CLEAR_TRIGGER(irq + i);
}
}
} else {
/* 0xf00 is only handled for 32-bit writes. */
goto bad_reg;
}
gic_update(s);
return;
bad_reg:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_dist_writeb: Bad offset %x\n", (int)offset);
}
static void gic_dist_writew(void *opaque, hwaddr offset,
uint32_t value)
{
gic_dist_writeb(opaque, offset, value & 0xff);
gic_dist_writeb(opaque, offset + 1, value >> 8);
}
static void gic_dist_writel(void *opaque, hwaddr offset,
uint32_t value)
{
GICState *s = (GICState *)opaque;
if (offset == 0xf00) {
int cpu;
int irq;
int mask;
cpu = gic_get_current_cpu(s);
irq = value & 0x3ff;
switch ((value >> 24) & 3) {
case 0:
mask = (value >> 16) & ALL_CPU_MASK;
break;
case 1:
mask = ALL_CPU_MASK ^ (1 << cpu);
break;
case 2:
mask = 1 << cpu;
break;
default:
DPRINTF("Bad Soft Int target filter\n");
mask = ALL_CPU_MASK;
break;
}
GIC_SET_PENDING(irq, mask);
gic_update(s);
return;
}
gic_dist_writew(opaque, offset, value & 0xffff);
gic_dist_writew(opaque, offset + 2, value >> 16);
}
static const MemoryRegionOps gic_dist_ops = {
.old_mmio = {
.read = { gic_dist_readb, gic_dist_readw, gic_dist_readl, },
.write = { gic_dist_writeb, gic_dist_writew, gic_dist_writel, },
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
{
switch (offset) {
case 0x00: /* Control */
return s->cpu_enabled[cpu];
case 0x04: /* Priority mask */
return s->priority_mask[cpu];
case 0x08: /* Binary Point */
/* ??? Not implemented. */
return 0;
case 0x0c: /* Acknowledge */
return gic_acknowledge_irq(s, cpu);
case 0x14: /* Running Priority */
return s->running_priority[cpu];
case 0x18: /* Highest Pending Interrupt */
return s->current_pending[cpu];
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
return 0;
}
}
static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
{
switch (offset) {
case 0x00: /* Control */
s->cpu_enabled[cpu] = (value & 1);
DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled[cpu] ? "En" : "Dis");
break;
case 0x04: /* Priority mask */
s->priority_mask[cpu] = (value & 0xff);
break;
case 0x08: /* Binary Point */
/* ??? Not implemented. */
break;
case 0x10: /* End Of Interrupt */
return gic_complete_irq(s, cpu, value & 0x3ff);
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_write: Bad offset %x\n", (int)offset);
return;
}
gic_update(s);
}
/* Wrappers to read/write the GIC CPU interface for the current CPU */
static uint64_t gic_thiscpu_read(void *opaque, hwaddr addr,
unsigned size)
{
GICState *s = (GICState *)opaque;
return gic_cpu_read(s, gic_get_current_cpu(s), addr);
}
static void gic_thiscpu_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
GICState *s = (GICState *)opaque;
gic_cpu_write(s, gic_get_current_cpu(s), addr, value);
}
/* Wrappers to read/write the GIC CPU interface for a specific CPU.
* These just decode the opaque pointer into GICState* + cpu id.
*/
static uint64_t gic_do_cpu_read(void *opaque, hwaddr addr,
unsigned size)
{
GICState **backref = (GICState **)opaque;
GICState *s = *backref;
int id = (backref - s->backref);
return gic_cpu_read(s, id, addr);
}
static void gic_do_cpu_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
GICState **backref = (GICState **)opaque;
GICState *s = *backref;
int id = (backref - s->backref);
gic_cpu_write(s, id, addr, value);
}
static const MemoryRegionOps gic_thiscpu_ops = {
.read = gic_thiscpu_read,
.write = gic_thiscpu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps gic_cpu_ops = {
.read = gic_do_cpu_read,
.write = gic_do_cpu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
void gic_init_irqs_and_distributor(GICState *s, int num_irq)
{
int i;
i = s->num_irq - GIC_INTERNAL;
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
* [0..N-1] SPIs
* [N..N+31] PPIs for CPU 0
* [N+32..N+63] PPIs for CPU 1
* ...
*/
if (s->revision != REV_NVIC) {
i += (GIC_INTERNAL * s->num_cpu);
}
qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i);
for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
}
memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000);
}
static void arm_gic_realize(DeviceState *dev, Error **errp)
{
/* Device instance realize function for the GIC sysbus device */
int i;
GICState *s = ARM_GIC(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
ARMGICClass *agc = ARM_GIC_GET_CLASS(s);
agc->parent_realize(dev, errp);
if (error_is_set(errp)) {
return;
}
gic_init_irqs_and_distributor(s, s->num_irq);
/* Memory regions for the CPU interfaces (NVIC doesn't have these):
* a region for "CPU interface for this core", then a region for
* "CPU interface for core 0", "for core 1", ...
* NB that the memory region size of 0x100 applies for the 11MPCore
* and also cores following the GIC v1 spec (ie A9).
* GIC v2 defines a larger memory region (0x1000) so this will need
* to be extended when we implement A15.
*/
memory_region_init_io(&s->cpuiomem[0], &gic_thiscpu_ops, s,
"gic_cpu", 0x100);
for (i = 0; i < NUM_CPU(s); i++) {
s->backref[i] = s;
memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i],
"gic_cpu", 0x100);
}
/* Distributor */
sysbus_init_mmio(sbd, &s->iomem);
/* cpu interfaces (one for "current cpu" plus one per cpu) */
for (i = 0; i <= NUM_CPU(s); i++) {
sysbus_init_mmio(sbd, &s->cpuiomem[i]);
}
}
static void arm_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ARMGICClass *agc = ARM_GIC_CLASS(klass);
dc->no_user = 1;
agc->parent_realize = dc->realize;
dc->realize = arm_gic_realize;
}
static const TypeInfo arm_gic_info = {
.name = TYPE_ARM_GIC,
.parent = TYPE_ARM_GIC_COMMON,
.instance_size = sizeof(GICState),
.class_init = arm_gic_class_init,
.class_size = sizeof(ARMGICClass),
};
static void arm_gic_register_types(void)
{
type_register_static(&arm_gic_info);
}
type_init(arm_gic_register_types)

176
hw/intc/arm_gic_common.c Normal file
View file

@ -0,0 +1,176 @@
/*
* ARM GIC support - common bits of emulated and KVM kernel model
*
* Copyright (c) 2012 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/arm_gic_internal.h"
static void gic_pre_save(void *opaque)
{
GICState *s = (GICState *)opaque;
ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
if (c->pre_save) {
c->pre_save(s);
}
}
static int gic_post_load(void *opaque, int version_id)
{
GICState *s = (GICState *)opaque;
ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
if (c->post_load) {
c->post_load(s);
}
return 0;
}
static const VMStateDescription vmstate_gic_irq_state = {
.name = "arm_gic_irq_state",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(enabled, gic_irq_state),
VMSTATE_UINT8(pending, gic_irq_state),
VMSTATE_UINT8(active, gic_irq_state),
VMSTATE_UINT8(level, gic_irq_state),
VMSTATE_BOOL(model, gic_irq_state),
VMSTATE_BOOL(trigger, gic_irq_state),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
.version_id = 4,
.minimum_version_id = 4,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
VMSTATE_BOOL(enabled, GICState),
VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, NCPU),
VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1,
vmstate_gic_irq_state, gic_irq_state),
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, NCPU),
VMSTATE_UINT16_ARRAY(priority_mask, GICState, NCPU),
VMSTATE_UINT16_ARRAY(running_irq, GICState, NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, NCPU),
VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU),
VMSTATE_END_OF_LIST()
}
};
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
GICState *s = ARM_GIC_COMMON(dev);
int num_irq = s->num_irq;
if (s->num_cpu > NCPU) {
error_setg(errp, "requested %u CPUs exceeds GIC maximum %d",
s->num_cpu, NCPU);
return;
}
s->num_irq += GIC_BASE_IRQ;
if (s->num_irq > GIC_MAXIRQ) {
error_setg(errp,
"requested %u interrupt lines exceeds GIC maximum %d",
num_irq, GIC_MAXIRQ);
return;
}
/* ITLinesNumber is represented as (N / 32) - 1 (see
* gic_dist_readb) so this is an implementation imposed
* restriction, not an architectural one:
*/
if (s->num_irq < 32 || (s->num_irq % 32)) {
error_setg(errp,
"%d interrupt lines unsupported: not divisible by 32",
num_irq);
return;
}
}
static void arm_gic_common_reset(DeviceState *dev)
{
GICState *s = FROM_SYSBUS(GICState, SYS_BUS_DEVICE(dev));
int i;
memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
for (i = 0 ; i < s->num_cpu; i++) {
if (s->revision == REV_11MPCORE) {
s->priority_mask[i] = 0xf0;
} else {
s->priority_mask[i] = 0;
}
s->current_pending[i] = 1023;
s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
s->cpu_enabled[i] = false;
}
for (i = 0; i < 16; i++) {
GIC_SET_ENABLED(i, ALL_CPU_MASK);
GIC_SET_TRIGGER(i);
}
if (s->num_cpu == 1) {
/* For uniprocessor GICs all interrupts always target the sole CPU */
for (i = 0; i < GIC_MAXIRQ; i++) {
s->irq_target[i] = 1;
}
}
s->enabled = false;
}
static Property arm_gic_common_properties[] = {
DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
/* Revision can be 1 or 2 for GIC architecture specification
* versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC.
* (Internally, 0xffffffff also indicates "not a GIC but an NVIC".)
*/
DEFINE_PROP_UINT32("revision", GICState, revision, 1),
DEFINE_PROP_END_OF_LIST(),
};
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = arm_gic_common_reset;
dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->vmsd = &vmstate_gic;
dc->no_user = 1;
}
static const TypeInfo arm_gic_common_type = {
.name = TYPE_ARM_GIC_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GICState),
.class_size = sizeof(ARMGICCommonClass),
.class_init = arm_gic_common_class_init,
.abstract = true,
};
static void register_types(void)
{
type_register_static(&arm_gic_common_type);
}
type_init(register_types)

167
hw/intc/arm_gic_kvm.c Normal file
View file

@ -0,0 +1,167 @@
/*
* ARM Generic Interrupt Controller using KVM in-kernel support
*
* Copyright (c) 2012 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "hw/arm_gic_internal.h"
#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
#define KVM_ARM_GIC(obj) \
OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
#define KVM_ARM_GIC_CLASS(klass) \
OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC)
#define KVM_ARM_GIC_GET_CLASS(obj) \
OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC)
typedef struct KVMARMGICClass {
ARMGICCommonClass parent_class;
DeviceRealize parent_realize;
void (*parent_reset)(DeviceState *dev);
} KVMARMGICClass;
static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
* [N..N+31] : PPI (internal) interrupts for CPU 0
* [N+32..N+63] : PPI (internal interrupts for CPU 1
* ...
* Convert this to the kernel's desired encoding, which
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
GICState *s = (GICState *)opaque;
int kvm_irq, irqtype, cpu;
if (irq < (s->num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
* hardware, with external interrupt IDs starting after the
* internal ones.
*/
irqtype = KVM_ARM_IRQ_TYPE_SPI;
cpu = 0;
irq += GIC_INTERNAL;
} else {
/* Internal interrupt: decode into (cpu, interrupt id) */
irqtype = KVM_ARM_IRQ_TYPE_PPI;
irq -= (s->num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT)
| (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq;
kvm_set_irq(kvm_state, kvm_irq, !!level);
}
static void kvm_arm_gic_put(GICState *s)
{
/* TODO: there isn't currently a kernel interface to set the GIC state */
}
static void kvm_arm_gic_get(GICState *s)
{
/* TODO: there isn't currently a kernel interface to get the GIC state */
}
static void kvm_arm_gic_reset(DeviceState *dev)
{
GICState *s = ARM_GIC_COMMON(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
kgc->parent_reset(dev);
kvm_arm_gic_put(s);
}
static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
{
int i;
GICState *s = KVM_ARM_GIC(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
kgc->parent_realize(dev, errp);
if (error_is_set(errp)) {
return;
}
i = s->num_irq - GIC_INTERNAL;
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
* [0..N-1] SPIs
* [N..N+31] PPIs for CPU 0
* [N+32..N+63] PPIs for CPU 1
* ...
*/
i += (GIC_INTERNAL * s->num_cpu);
qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
/* We never use our outbound IRQ lines but provide them so that
* we maintain the same interface as the non-KVM GIC.
*/
for (i = 0; i < s->num_cpu; i++) {
sysbus_init_irq(sbd, &s->parent_irq[i]);
}
/* Distributor */
memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
kvm_arm_register_device(&s->iomem,
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_DIST);
/* CPU interface for current core. Unlike arm_gic, we don't
* provide the "interface for core #N" memory regions, because
* cores with a VGIC don't have those.
*/
memory_region_init_reservation(&s->cpuiomem[0], "kvm-gic_cpu", 0x1000);
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
kvm_arm_register_device(&s->cpuiomem[0],
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_CPU);
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass);
KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass);
agcc->pre_save = kvm_arm_gic_get;
agcc->post_load = kvm_arm_gic_put;
kgc->parent_realize = dc->realize;
kgc->parent_reset = dc->reset;
dc->realize = kvm_arm_gic_realize;
dc->reset = kvm_arm_gic_reset;
dc->no_user = 1;
}
static const TypeInfo kvm_arm_gic_info = {
.name = TYPE_KVM_ARM_GIC,
.parent = TYPE_ARM_GIC_COMMON,
.instance_size = sizeof(GICState),
.class_init = kvm_arm_gic_class_init,
.class_size = sizeof(KVMARMGICClass),
};
static void kvm_arm_gic_register_types(void)
{
type_register_static(&kvm_arm_gic_info);
}
type_init(kvm_arm_gic_register_types)

553
hw/intc/armv7m_nvic.c Normal file
View file

@ -0,0 +1,553 @@
/*
* ARM Nested Vectored Interrupt Controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*
* The ARMv7M System controller is fairly tightly tied in with the
* NVIC. Much of that is also implemented here.
*/
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "hw/arm.h"
#include "exec/address-spaces.h"
#include "hw/arm_gic_internal.h"
typedef struct {
GICState gic;
struct {
uint32_t control;
uint32_t reload;
int64_t tick;
QEMUTimer *timer;
} systick;
MemoryRegion sysregmem;
MemoryRegion gic_iomem_alias;
MemoryRegion container;
uint32_t num_irq;
} nvic_state;
#define TYPE_NVIC "armv7m_nvic"
/**
* NVICClass:
* @parent_reset: the parent class' reset handler.
*
* A model of the v7M NVIC and System Controller
*/
typedef struct NVICClass {
/*< private >*/
ARMGICClass parent_class;
/*< public >*/
DeviceRealize parent_realize;
void (*parent_reset)(DeviceState *dev);
} NVICClass;
#define NVIC_CLASS(klass) \
OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC)
#define NVIC_GET_CLASS(obj) \
OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC)
#define NVIC(obj) \
OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC)
static const uint8_t nvic_id[] = {
0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1
};
/* qemu timers run at 1GHz. We want something closer to 1MHz. */
#define SYSTICK_SCALE 1000ULL
#define SYSTICK_ENABLE (1 << 0)
#define SYSTICK_TICKINT (1 << 1)
#define SYSTICK_CLKSOURCE (1 << 2)
#define SYSTICK_COUNTFLAG (1 << 16)
int system_clock_scale;
/* Conversion factor from qemu timer to SysTick frequencies. */
static inline int64_t systick_scale(nvic_state *s)
{
if (s->systick.control & SYSTICK_CLKSOURCE)
return system_clock_scale;
else
return 1000;
}
static void systick_reload(nvic_state *s, int reset)
{
if (reset)
s->systick.tick = qemu_get_clock_ns(vm_clock);
s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
qemu_mod_timer(s->systick.timer, s->systick.tick);
}
static void systick_timer_tick(void * opaque)
{
nvic_state *s = (nvic_state *)opaque;
s->systick.control |= SYSTICK_COUNTFLAG;
if (s->systick.control & SYSTICK_TICKINT) {
/* Trigger the interrupt. */
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
}
if (s->systick.reload == 0) {
s->systick.control &= ~SYSTICK_ENABLE;
} else {
systick_reload(s, 0);
}
}
static void systick_reset(nvic_state *s)
{
s->systick.control = 0;
s->systick.reload = 0;
s->systick.tick = 0;
qemu_del_timer(s->systick.timer);
}
/* The external routines use the hardware vector numbering, ie. the first
IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
void armv7m_nvic_set_pending(void *opaque, int irq)
{
nvic_state *s = (nvic_state *)opaque;
if (irq >= 16)
irq += 16;
gic_set_pending_private(&s->gic, 0, irq);
}
/* Make pending IRQ active. */
int armv7m_nvic_acknowledge_irq(void *opaque)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t irq;
irq = gic_acknowledge_irq(&s->gic, 0);
if (irq == 1023)
hw_error("Interrupt but no vector\n");
if (irq >= 32)
irq -= 16;
return irq;
}
void armv7m_nvic_complete_irq(void *opaque, int irq)
{
nvic_state *s = (nvic_state *)opaque;
if (irq >= 16)
irq += 16;
gic_complete_irq(&s->gic, 0, irq);
}
static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
{
uint32_t val;
int irq;
switch (offset) {
case 4: /* Interrupt Control Type. */
return (s->num_irq / 32) - 1;
case 0x10: /* SysTick Control and Status. */
val = s->systick.control;
s->systick.control &= ~SYSTICK_COUNTFLAG;
return val;
case 0x14: /* SysTick Reload Value. */
return s->systick.reload;
case 0x18: /* SysTick Current Value. */
{
int64_t t;
if ((s->systick.control & SYSTICK_ENABLE) == 0)
return 0;
t = qemu_get_clock_ns(vm_clock);
if (t >= s->systick.tick)
return 0;
val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
/* The interrupt in triggered when the timer reaches zero.
However the counter is not reloaded until the next clock
tick. This is a hack to return zero during the first tick. */
if (val > s->systick.reload)
val = 0;
return val;
}
case 0x1c: /* SysTick Calibration Value. */
return 10000;
case 0xd00: /* CPUID Base. */
return cpu_single_env->cp15.c0_cpuid;
case 0xd04: /* Interrypt Control State. */
/* VECTACTIVE */
val = s->gic.running_irq[0];
if (val == 1023) {
val = 0;
} else if (val >= 32) {
val -= 16;
}
/* RETTOBASE */
if (s->gic.running_irq[0] == 1023
|| s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
val |= (1 << 11);
}
/* VECTPENDING */
if (s->gic.current_pending[0] != 1023)
val |= (s->gic.current_pending[0] << 12);
/* ISRPENDING */
for (irq = 32; irq < s->num_irq; irq++) {
if (s->gic.irq_state[irq].pending) {
val |= (1 << 22);
break;
}
}
/* PENDSTSET */
if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
val |= (1 << 26);
/* PENDSVSET */
if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending)
val |= (1 << 28);
/* NMIPENDSET */
if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending)
val |= (1 << 31);
return val;
case 0xd08: /* Vector Table Offset. */
return cpu_single_env->v7m.vecbase;
case 0xd0c: /* Application Interrupt/Reset Control. */
return 0xfa05000;
case 0xd10: /* System Control. */
/* TODO: Implement SLEEPONEXIT. */
return 0;
case 0xd14: /* Configuration Control. */
/* TODO: Implement Configuration Control bits. */
return 0;
case 0xd24: /* System Handler Status. */
val = 0;
if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
return val;
case 0xd28: /* Configurable Fault Status. */
/* TODO: Implement Fault Status. */
qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n");
return 0;
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
/* TODO: Implement fault status registers. */
qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n");
return 0;
case 0xd40: /* PFR0. */
return 0x00000030;
case 0xd44: /* PRF1. */
return 0x00000200;
case 0xd48: /* DFR0. */
return 0x00100000;
case 0xd4c: /* AFR0. */
return 0x00000000;
case 0xd50: /* MMFR0. */
return 0x00000030;
case 0xd54: /* MMFR1. */
return 0x00000000;
case 0xd58: /* MMFR2. */
return 0x00000000;
case 0xd5c: /* MMFR3. */
return 0x00000000;
case 0xd60: /* ISAR0. */
return 0x01141110;
case 0xd64: /* ISAR1. */
return 0x02111000;
case 0xd68: /* ISAR2. */
return 0x21112231;
case 0xd6c: /* ISAR3. */
return 0x01111110;
case 0xd70: /* ISAR4. */
return 0x01310102;
/* TODO: Implement debug registers. */
default:
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
return 0;
}
}
static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
{
uint32_t oldval;
switch (offset) {
case 0x10: /* SysTick Control and Status. */
oldval = s->systick.control;
s->systick.control &= 0xfffffff8;
s->systick.control |= value & 7;
if ((oldval ^ value) & SYSTICK_ENABLE) {
int64_t now = qemu_get_clock_ns(vm_clock);
if (value & SYSTICK_ENABLE) {
if (s->systick.tick) {
s->systick.tick += now;
qemu_mod_timer(s->systick.timer, s->systick.tick);
} else {
systick_reload(s, 1);
}
} else {
qemu_del_timer(s->systick.timer);
s->systick.tick -= now;
if (s->systick.tick < 0)
s->systick.tick = 0;
}
} else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
/* This is a hack. Force the timer to be reloaded
when the reference clock is changed. */
systick_reload(s, 1);
}
break;
case 0x14: /* SysTick Reload Value. */
s->systick.reload = value;
break;
case 0x18: /* SysTick Current Value. Writes reload the timer. */
systick_reload(s, 1);
s->systick.control &= ~SYSTICK_COUNTFLAG;
break;
case 0xd04: /* Interrupt Control State. */
if (value & (1 << 31)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
}
if (value & (1 << 28)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
} else if (value & (1 << 27)) {
s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
gic_update(&s->gic);
}
if (value & (1 << 26)) {
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
} else if (value & (1 << 25)) {
s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
gic_update(&s->gic);
}
break;
case 0xd08: /* Vector Table Offset. */
cpu_single_env->v7m.vecbase = value & 0xffffff80;
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
if ((value >> 16) == 0x05fa) {
if (value & 2) {
qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
}
if (value & 5) {
qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
}
}
break;
case 0xd10: /* System Control. */
case 0xd14: /* Configuration Control. */
/* TODO: Implement control registers. */
qemu_log_mask(LOG_UNIMP, "NVIC: SCR and CCR unimplemented\n");
break;
case 0xd24: /* System Handler Control. */
/* TODO: Real hardware allows you to set/clear the active bits
under some circumstances. We don't implement this. */
s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
break;
case 0xd28: /* Configurable Fault Status. */
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
qemu_log_mask(LOG_UNIMP,
"NVIC: fault status registers unimplemented\n");
break;
case 0xf00: /* Software Triggered Interrupt Register */
if ((value & 0x1ff) < s->num_irq) {
gic_set_pending_private(&s->gic, 0, value & 0x1ff);
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"NVIC: Bad write offset 0x%x\n", offset);
}
}
static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr,
unsigned size)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t offset = addr;
int i;
uint32_t val;
switch (offset) {
case 0xd18 ... 0xd23: /* System Handler Priority. */
val = 0;
for (i = 0; i < size; i++) {
val |= s->gic.priority1[(offset - 0xd14) + i][0] << (i * 8);
}
return val;
case 0xfe0 ... 0xfff: /* ID. */
if (offset & 3) {
return 0;
}
return nvic_id[(offset - 0xfe0) >> 2];
}
if (size == 4) {
return nvic_readl(s, offset);
}
qemu_log_mask(LOG_GUEST_ERROR,
"NVIC: Bad read of size %d at offset 0x%x\n", size, offset);
return 0;
}
static void nvic_sysreg_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
nvic_state *s = (nvic_state *)opaque;
uint32_t offset = addr;
int i;
switch (offset) {
case 0xd18 ... 0xd23: /* System Handler Priority. */
for (i = 0; i < size; i++) {
s->gic.priority1[(offset - 0xd14) + i][0] =
(value >> (i * 8)) & 0xff;
}
gic_update(&s->gic);
return;
}
if (size == 4) {
nvic_writel(s, offset, value);
return;
}
qemu_log_mask(LOG_GUEST_ERROR,
"NVIC: Bad write of size %d at offset 0x%x\n", size, offset);
}
static const MemoryRegionOps nvic_sysreg_ops = {
.read = nvic_sysreg_read,
.write = nvic_sysreg_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_nvic = {
.name = "armv7m_nvic",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(systick.control, nvic_state),
VMSTATE_UINT32(systick.reload, nvic_state),
VMSTATE_INT64(systick.tick, nvic_state),
VMSTATE_TIMER(systick.timer, nvic_state),
VMSTATE_END_OF_LIST()
}
};
static void armv7m_nvic_reset(DeviceState *dev)
{
nvic_state *s = NVIC(dev);
NVICClass *nc = NVIC_GET_CLASS(s);
nc->parent_reset(dev);
/* Common GIC reset resets to disabled; the NVIC doesn't have
* per-CPU interfaces so mark our non-existent CPU interface
* as enabled by default, and with a priority mask which allows
* all interrupts through.
*/
s->gic.cpu_enabled[0] = true;
s->gic.priority_mask[0] = 0x100;
/* The NVIC as a whole is always enabled. */
s->gic.enabled = true;
systick_reset(s);
}
static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
{
nvic_state *s = NVIC(dev);
NVICClass *nc = NVIC_GET_CLASS(s);
/* The NVIC always has only one CPU */
s->gic.num_cpu = 1;
/* Tell the common code we're an NVIC */
s->gic.revision = 0xffffffff;
s->num_irq = s->gic.num_irq;
nc->parent_realize(dev, errp);
if (error_is_set(errp)) {
return;
}
gic_init_irqs_and_distributor(&s->gic, s->num_irq);
/* The NVIC and system controller register area looks like this:
* 0..0xff : system control registers, including systick
* 0x100..0xcff : GIC-like registers
* 0xd00..0xfff : system control registers
* We use overlaying to put the GIC like registers
* over the top of the system control register region.
*/
memory_region_init(&s->container, "nvic", 0x1000);
/* The system register region goes at the bottom of the priority
* stack as it covers the whole page.
*/
memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s,
"nvic_sysregs", 0x1000);
memory_region_add_subregion(&s->container, 0, &s->sysregmem);
/* Alias the GIC region so we can get only the section of it
* we need, and layer it on top of the system register region.
*/
memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem,
0x100, 0xc00);
memory_region_add_subregion_overlap(&s->container, 0x100,
&s->gic_iomem_alias, 1);
/* Map the whole thing into system memory at the location required
* by the v7M architecture.
*/
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container);
s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s);
}
static void armv7m_nvic_instance_init(Object *obj)
{
/* We have a different default value for the num-irq property
* than our superclass. This function runs after qdev init
* has set the defaults from the Property array and before
* any user-specified property setting, so just modify the
* value in the GICState struct.
*/
GICState *s = ARM_GIC_COMMON(obj);
/* The ARM v7m may have anything from 0 to 496 external interrupt
* IRQ lines. We default to 64. Other boards may differ and should
* set the num-irq property appropriately.
*/
s->num_irq = 64;
}
static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
{
NVICClass *nc = NVIC_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
nc->parent_reset = dc->reset;
nc->parent_realize = dc->realize;
dc->vmsd = &vmstate_nvic;
dc->reset = armv7m_nvic_reset;
dc->realize = armv7m_nvic_realize;
}
static const TypeInfo armv7m_nvic_info = {
.name = TYPE_NVIC,
.parent = TYPE_ARM_GIC_COMMON,
.instance_init = armv7m_nvic_instance_init,
.instance_size = sizeof(nvic_state),
.class_init = armv7m_nvic_class_init,
.class_size = sizeof(NVICClass),
};
static void armv7m_nvic_register_types(void)
{
type_register_static(&armv7m_nvic_info);
}
type_init(armv7m_nvic_register_types)

180
hw/intc/etraxfs_pic.c Normal file
View file

@ -0,0 +1,180 @@
/*
* QEMU ETRAX Interrupt Controller.
*
* Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/sysbus.h"
#include "hw/hw.h"
//#include "pc.h"
//#include "etraxfs.h"
#define D(x)
#define R_RW_MASK 0
#define R_R_VECT 1
#define R_R_MASKED_VECT 2
#define R_R_NMI 3
#define R_R_GURU 4
#define R_MAX 5
struct etrax_pic
{
SysBusDevice busdev;
MemoryRegion mmio;
void *interrupt_vector;
qemu_irq parent_irq;
qemu_irq parent_nmi;
uint32_t regs[R_MAX];
};
static void pic_update(struct etrax_pic *fs)
{
uint32_t vector = 0;
int i;
fs->regs[R_R_MASKED_VECT] = fs->regs[R_R_VECT] & fs->regs[R_RW_MASK];
/* The ETRAX interrupt controller signals interrupts to the core
through an interrupt request wire and an irq vector bus. If
multiple interrupts are simultaneously active it chooses vector
0x30 and lets the sw choose the priorities. */
if (fs->regs[R_R_MASKED_VECT]) {
uint32_t mv = fs->regs[R_R_MASKED_VECT];
for (i = 0; i < 31; i++) {
if (mv & 1) {
vector = 0x31 + i;
/* Check for multiple interrupts. */
if (mv > 1)
vector = 0x30;
break;
}
mv >>= 1;
}
}
if (fs->interrupt_vector) {
/* hack alert: ptr property */
*(uint32_t*)(fs->interrupt_vector) = vector;
}
qemu_set_irq(fs->parent_irq, !!vector);
}
static uint64_t
pic_read(void *opaque, hwaddr addr, unsigned int size)
{
struct etrax_pic *fs = opaque;
uint32_t rval;
rval = fs->regs[addr >> 2];
D(printf("%s %x=%x\n", __func__, addr, rval));
return rval;
}
static void pic_write(void *opaque, hwaddr addr,
uint64_t value, unsigned int size)
{
struct etrax_pic *fs = opaque;
D(printf("%s addr=%x val=%x\n", __func__, addr, value));
if (addr == R_RW_MASK) {
fs->regs[R_RW_MASK] = value;
pic_update(fs);
}
}
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 nmi_handler(void *opaque, int irq, int level)
{
struct etrax_pic *fs = (void *)opaque;
uint32_t mask;
mask = 1 << irq;
if (level)
fs->regs[R_R_NMI] |= mask;
else
fs->regs[R_R_NMI] &= ~mask;
qemu_set_irq(fs->parent_nmi, !!fs->regs[R_R_NMI]);
}
static void irq_handler(void *opaque, int irq, int level)
{
struct etrax_pic *fs = (void *)opaque;
if (irq >= 30)
return nmi_handler(opaque, irq, level);
irq -= 1;
fs->regs[R_R_VECT] &= ~(1 << irq);
fs->regs[R_R_VECT] |= (!!level << irq);
pic_update(fs);
}
static int etraxfs_pic_init(SysBusDevice *dev)
{
struct etrax_pic *s = FROM_SYSBUS(typeof (*s), dev);
qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
sysbus_init_irq(dev, &s->parent_irq);
sysbus_init_irq(dev, &s->parent_nmi);
memory_region_init_io(&s->mmio, &pic_ops, s, "etraxfs-pic", R_MAX * 4);
sysbus_init_mmio(dev, &s->mmio);
return 0;
}
static Property etraxfs_pic_properties[] = {
DEFINE_PROP_PTR("interrupt_vector", struct etrax_pic, interrupt_vector),
DEFINE_PROP_END_OF_LIST(),
};
static void etraxfs_pic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = etraxfs_pic_init;
dc->props = etraxfs_pic_properties;
}
static const TypeInfo etraxfs_pic_info = {
.name = "etraxfs,pic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct etrax_pic),
.class_init = etraxfs_pic_class_init,
};
static void etraxfs_pic_register_types(void)
{
type_register_static(&etraxfs_pic_info);
}
type_init(etraxfs_pic_register_types)

View file

@ -0,0 +1,455 @@
/*
* Samsung exynos4210 Interrupt Combiner
*
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Evgeny Voevodin <e.voevodin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
* IRQ sources into groups and provides signal output to GIC from each group. It
* is driven by common mask and enable/disable logic. Take a note that not all
* IRQs are passed to GIC through Combiner.
*/
#include "hw/sysbus.h"
#include "hw/arm/exynos4210.h"
//#define DEBUG_COMBINER
#ifdef DEBUG_COMBINER
#define DPRINTF(fmt, ...) \
do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while (0)
#endif
#define IIC_NGRP 64 /* Internal Interrupt Combiner
Groups number */
#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner
Interrupts number */
#define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */
#define IIC_REGSET_SIZE 0x41
/*
* State for each output signal of internal combiner
*/
typedef struct CombinerGroupState {
uint8_t src_mask; /* 1 - source enabled, 0 - disabled */
uint8_t src_pending; /* Pending source interrupts before masking */
} CombinerGroupState;
typedef struct Exynos4210CombinerState {
SysBusDevice busdev;
MemoryRegion iomem;
struct CombinerGroupState group[IIC_NGRP];
uint32_t reg_set[IIC_REGSET_SIZE];
uint32_t icipsr[2];
uint32_t external; /* 1 means that this combiner is external */
qemu_irq output_irq[IIC_NGRP];
} Exynos4210CombinerState;
static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
.name = "exynos4210.combiner.groupstate",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(src_mask, CombinerGroupState),
VMSTATE_UINT8(src_pending, CombinerGroupState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_exynos4210_combiner = {
.name = "exynos4210.combiner",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
vmstate_exynos4210_combiner_group_state, CombinerGroupState),
VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
IIC_REGSET_SIZE),
VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
VMSTATE_UINT32(external, Exynos4210CombinerState),
VMSTATE_END_OF_LIST()
}
};
/*
* Get Combiner input GPIO into irqs structure
*/
void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
int ext)
{
int n;
int bit;
int max;
qemu_irq *irq;
max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
/*
* Some IRQs of Int/External Combiner are going to two Combiners groups,
* so let split them.
*/
for (n = 0; n < max; n++) {
bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
switch (n) {
/* MDNIE_LCD1 INTG1 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
continue;
/* TMU INTG3 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
continue;
/* LCD1 INTG12 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
continue;
/* Multi-Core Timer INTG12 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG35 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG51 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
/* Multi-Core Timer INTG53 */
case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
continue;
}
irq[n] = qdev_get_gpio_in(dev, n);
}
}
static uint64_t
exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
get a start of corresponding group quad */
uint32_t grp_quad_base_n; /* Base of group quad */
uint32_t reg_n; /* Register number inside the quad */
uint32_t val;
req_quad_base_n = offset >> 4;
grp_quad_base_n = req_quad_base_n << 2;
reg_n = (offset - (req_quad_base_n << 4)) >> 2;
if (req_quad_base_n >= IIC_NGRP) {
/* Read of ICIPSR register */
return s->icipsr[reg_n];
}
val = 0;
switch (reg_n) {
/* IISTR */
case 2:
val |= s->group[grp_quad_base_n].src_pending;
val |= s->group[grp_quad_base_n + 1].src_pending << 8;
val |= s->group[grp_quad_base_n + 2].src_pending << 16;
val |= s->group[grp_quad_base_n + 3].src_pending << 24;
break;
/* IIMSR */
case 3:
val |= s->group[grp_quad_base_n].src_mask &
s->group[grp_quad_base_n].src_pending;
val |= (s->group[grp_quad_base_n + 1].src_mask &
s->group[grp_quad_base_n + 1].src_pending) << 8;
val |= (s->group[grp_quad_base_n + 2].src_mask &
s->group[grp_quad_base_n + 2].src_pending) << 16;
val |= (s->group[grp_quad_base_n + 3].src_mask &
s->group[grp_quad_base_n + 3].src_pending) << 24;
break;
default:
if (offset >> 2 >= IIC_REGSET_SIZE) {
hw_error("exynos4210.combiner: overflow of reg_set by 0x"
TARGET_FMT_plx "offset\n", offset);
}
val = s->reg_set[offset >> 2];
return 0;
}
return val;
}
static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
/* Send interrupt if needed */
if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
#ifdef DEBUG_COMBINER
if (group_n != 26) {
/* skip uart */
DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
}
#endif
/* Set Combiner interrupt pending status after masking */
if (group_n >= 32) {
s->icipsr[1] |= 1 << (group_n - 32);
} else {
s->icipsr[0] |= 1 << group_n;
}
qemu_irq_raise(s->output_irq[group_n]);
} else {
#ifdef DEBUG_COMBINER
if (group_n != 26) {
/* skip uart */
DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
}
#endif
/* Set Combiner interrupt pending status after masking */
if (group_n >= 32) {
s->icipsr[1] &= ~(1 << (group_n - 32));
} else {
s->icipsr[0] &= ~(1 << group_n);
}
qemu_irq_lower(s->output_irq[group_n]);
}
}
static void exynos4210_combiner_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
get a start of corresponding group quad */
uint32_t grp_quad_base_n; /* Base of group quad */
uint32_t reg_n; /* Register number inside the quad */
req_quad_base_n = offset >> 4;
grp_quad_base_n = req_quad_base_n << 2;
reg_n = (offset - (req_quad_base_n << 4)) >> 2;
if (req_quad_base_n >= IIC_NGRP) {
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
return;
}
if (reg_n > 1) {
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
return;
}
if (offset >> 2 >= IIC_REGSET_SIZE) {
hw_error("exynos4210.combiner: overflow of reg_set by 0x"
TARGET_FMT_plx "offset\n", offset);
}
s->reg_set[offset >> 2] = val;
switch (reg_n) {
/* IIESR */
case 0:
/* FIXME: what if irq is pending, allowed by mask, and we allow it
* again. Interrupt will rise again! */
DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
s->external ? "EXT" : "INT",
grp_quad_base_n,
grp_quad_base_n + 1,
grp_quad_base_n + 2,
grp_quad_base_n + 3);
/* Enable interrupt sources */
s->group[grp_quad_base_n].src_mask |= val & 0xFF;
s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
exynos4210_combiner_update(s, grp_quad_base_n);
exynos4210_combiner_update(s, grp_quad_base_n + 1);
exynos4210_combiner_update(s, grp_quad_base_n + 2);
exynos4210_combiner_update(s, grp_quad_base_n + 3);
break;
/* IIECR */
case 1:
DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
s->external ? "EXT" : "INT",
grp_quad_base_n,
grp_quad_base_n + 1,
grp_quad_base_n + 2,
grp_quad_base_n + 3);
/* Disable interrupt sources */
s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
exynos4210_combiner_update(s, grp_quad_base_n);
exynos4210_combiner_update(s, grp_quad_base_n + 1);
exynos4210_combiner_update(s, grp_quad_base_n + 2);
exynos4210_combiner_update(s, grp_quad_base_n + 3);
break;
default:
hw_error("exynos4210.combiner: unallowed write access at offset 0x"
TARGET_FMT_plx "\n", offset);
break;
}
}
/* Get combiner group and bit from irq number */
static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
{
*bit = irq - ((irq >> 3) << 3);
return irq >> 3;
}
/* Process a change in an external IRQ input. */
static void exynos4210_combiner_handler(void *opaque, int irq, int level)
{
struct Exynos4210CombinerState *s =
(struct Exynos4210CombinerState *)opaque;
uint8_t bit_n, group_n;
group_n = get_combiner_group_and_bit(irq, &bit_n);
if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
, group_n);
return;
}
if (level) {
s->group[group_n].src_pending |= 1 << bit_n;
} else {
s->group[group_n].src_pending &= ~(1 << bit_n);
}
exynos4210_combiner_update(s, group_n);
}
static void exynos4210_combiner_reset(DeviceState *d)
{
struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
memset(&s->group, 0, sizeof(s->group));
memset(&s->reg_set, 0, sizeof(s->reg_set));
s->reg_set[0xC0 >> 2] = 0x01010101;
s->reg_set[0xC4 >> 2] = 0x01010101;
s->reg_set[0xD0 >> 2] = 0x01010101;
s->reg_set[0xD4 >> 2] = 0x01010101;
}
static const MemoryRegionOps exynos4210_combiner_ops = {
.read = exynos4210_combiner_read,
.write = exynos4210_combiner_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* Internal Combiner initialization.
*/
static int exynos4210_combiner_init(SysBusDevice *dev)
{
unsigned int i;
struct Exynos4210CombinerState *s =
FROM_SYSBUS(struct Exynos4210CombinerState, dev);
/* Allocate general purpose input signals and connect a handler to each of
* them */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
/* Connect SysBusDev irqs to device specific irqs */
for (i = 0; i < IIC_NIRQ; i++) {
sysbus_init_irq(dev, &s->output_irq[i]);
}
memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
"exynos4210-combiner", IIC_REGION_SIZE);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static Property exynos4210_combiner_properties[] = {
DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_combiner_init;
dc->reset = exynos4210_combiner_reset;
dc->props = exynos4210_combiner_properties;
dc->vmsd = &vmstate_exynos4210_combiner;
}
static const TypeInfo exynos4210_combiner_info = {
.name = "exynos4210.combiner",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210CombinerState),
.class_init = exynos4210_combiner_class_init,
};
static void exynos4210_combiner_register_types(void)
{
type_register_static(&exynos4210_combiner_info);
}
type_init(exynos4210_combiner_register_types)

462
hw/intc/exynos4210_gic.c Normal file
View file

@ -0,0 +1,462 @@
/*
* Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
*
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Evgeny Voevodin <e.voevodin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/sysbus.h"
#include "qemu-common.h"
#include "hw/irq.h"
#include "hw/arm/exynos4210.h"
enum ExtGicId {
EXT_GIC_ID_MDMA_LCD0 = 66,
EXT_GIC_ID_PDMA0,
EXT_GIC_ID_PDMA1,
EXT_GIC_ID_TIMER0,
EXT_GIC_ID_TIMER1,
EXT_GIC_ID_TIMER2,
EXT_GIC_ID_TIMER3,
EXT_GIC_ID_TIMER4,
EXT_GIC_ID_MCT_L0,
EXT_GIC_ID_WDT,
EXT_GIC_ID_RTC_ALARM,
EXT_GIC_ID_RTC_TIC,
EXT_GIC_ID_GPIO_XB,
EXT_GIC_ID_GPIO_XA,
EXT_GIC_ID_MCT_L1,
EXT_GIC_ID_IEM_APC,
EXT_GIC_ID_IEM_IEC,
EXT_GIC_ID_NFC,
EXT_GIC_ID_UART0,
EXT_GIC_ID_UART1,
EXT_GIC_ID_UART2,
EXT_GIC_ID_UART3,
EXT_GIC_ID_UART4,
EXT_GIC_ID_MCT_G0,
EXT_GIC_ID_I2C0,
EXT_GIC_ID_I2C1,
EXT_GIC_ID_I2C2,
EXT_GIC_ID_I2C3,
EXT_GIC_ID_I2C4,
EXT_GIC_ID_I2C5,
EXT_GIC_ID_I2C6,
EXT_GIC_ID_I2C7,
EXT_GIC_ID_SPI0,
EXT_GIC_ID_SPI1,
EXT_GIC_ID_SPI2,
EXT_GIC_ID_MCT_G1,
EXT_GIC_ID_USB_HOST,
EXT_GIC_ID_USB_DEVICE,
EXT_GIC_ID_MODEMIF,
EXT_GIC_ID_HSMMC0,
EXT_GIC_ID_HSMMC1,
EXT_GIC_ID_HSMMC2,
EXT_GIC_ID_HSMMC3,
EXT_GIC_ID_SDMMC,
EXT_GIC_ID_MIPI_CSI_4LANE,
EXT_GIC_ID_MIPI_DSI_4LANE,
EXT_GIC_ID_MIPI_CSI_2LANE,
EXT_GIC_ID_MIPI_DSI_2LANE,
EXT_GIC_ID_ONENAND_AUDI,
EXT_GIC_ID_ROTATOR,
EXT_GIC_ID_FIMC0,
EXT_GIC_ID_FIMC1,
EXT_GIC_ID_FIMC2,
EXT_GIC_ID_FIMC3,
EXT_GIC_ID_JPEG,
EXT_GIC_ID_2D,
EXT_GIC_ID_PCIe,
EXT_GIC_ID_MIXER,
EXT_GIC_ID_HDMI,
EXT_GIC_ID_HDMI_I2C,
EXT_GIC_ID_MFC,
EXT_GIC_ID_TVENC,
};
enum ExtInt {
EXT_GIC_ID_EXTINT0 = 48,
EXT_GIC_ID_EXTINT1,
EXT_GIC_ID_EXTINT2,
EXT_GIC_ID_EXTINT3,
EXT_GIC_ID_EXTINT4,
EXT_GIC_ID_EXTINT5,
EXT_GIC_ID_EXTINT6,
EXT_GIC_ID_EXTINT7,
EXT_GIC_ID_EXTINT8,
EXT_GIC_ID_EXTINT9,
EXT_GIC_ID_EXTINT10,
EXT_GIC_ID_EXTINT11,
EXT_GIC_ID_EXTINT12,
EXT_GIC_ID_EXTINT13,
EXT_GIC_ID_EXTINT14,
EXT_GIC_ID_EXTINT15
};
/*
* External GIC sources which are not from External Interrupt Combiner or
* External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
* which is INTG16 in Internal Interrupt Combiner.
*/
static uint32_t
combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
/* int combiner groups 16-19 */
{ }, { }, { }, { },
/* int combiner group 20 */
{ 0, EXT_GIC_ID_MDMA_LCD0 },
/* int combiner group 21 */
{ EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
/* int combiner group 22 */
{ EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
/* int combiner group 23 */
{ EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
/* int combiner group 24 */
{ EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
/* int combiner group 25 */
{ EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
/* int combiner group 26 */
{ EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
EXT_GIC_ID_UART4 },
/* int combiner group 27 */
{ EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
EXT_GIC_ID_I2C7 },
/* int combiner group 28 */
{ EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST},
/* int combiner group 29 */
{ EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
/* int combiner group 30 */
{ EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
/* int combiner group 31 */
{ EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
/* int combiner group 32 */
{ EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
/* int combiner group 33 */
{ EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
/* int combiner group 34 */
{ EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
/* int combiner group 35 */
{ 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* int combiner group 36 */
{ EXT_GIC_ID_MIXER },
/* int combiner group 37 */
{ EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
EXT_GIC_ID_EXTINT7 },
/* groups 38-50 */
{ }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
/* int combiner group 51 */
{ EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* group 52 */
{ },
/* int combiner group 53 */
{ EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
/* groups 54-63 */
{ }, { }, { }, { }, { }, { }, { }, { }, { }, { }
};
#define EXYNOS4210_GIC_NIRQ 160
#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000
#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000
#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET 0x8000
#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100
#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
static void exynos4210_irq_handler(void *opaque, int irq, int level)
{
Exynos4210Irq *s = (Exynos4210Irq *)opaque;
/* Bypass */
qemu_set_irq(s->board_irqs[irq], level);
}
/*
* Initialize exynos4210 IRQ subsystem stub.
*/
qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
{
return qemu_allocate_irqs(exynos4210_irq_handler, s,
EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
}
/*
* Initialize board IRQs.
* These IRQs contain splitted Int/External Combiner and External Gic IRQs.
*/
void exynos4210_init_board_irqs(Exynos4210Irq *s)
{
uint32_t grp, bit, irq_id, n;
for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_combiner_irq[n]);
irq_id = 0;
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
/* MCT_G0 is passed to External GIC */
irq_id = EXT_GIC_ID_MCT_G0;
}
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
/* MCT_G1 is passed to External and GIC */
irq_id = EXT_GIC_ID_MCT_G1;
}
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
}
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
/* these IDs are passed to Internal Combiner and External GIC */
grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
irq_id = combiner_grp_to_gic_id[grp -
EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
}
}
}
/*
* Get IRQ number from exynos4210 IRQ subsystem stub.
* To identify IRQ source use internal combiner group and bit number
* grp - group number
* bit - bit number inside group
*/
uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
{
return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
}
/********* GIC part *********/
typedef struct {
SysBusDevice busdev;
MemoryRegion cpu_container;
MemoryRegion dist_container;
MemoryRegion cpu_alias[EXYNOS4210_NCPUS];
MemoryRegion dist_alias[EXYNOS4210_NCPUS];
uint32_t num_cpu;
DeviceState *gic;
} Exynos4210GicState;
static void exynos4210_gic_set_irq(void *opaque, int irq, int level)
{
Exynos4210GicState *s = (Exynos4210GicState *)opaque;
qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
}
static int exynos4210_gic_init(SysBusDevice *dev)
{
Exynos4210GicState *s = FROM_SYSBUS(Exynos4210GicState, dev);
uint32_t i;
const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
const char dist_prefix[] = "exynos4210-gic-alias_dist";
char cpu_alias_name[sizeof(cpu_prefix) + 3];
char dist_alias_name[sizeof(cpu_prefix) + 3];
SysBusDevice *busdev;
s->gic = qdev_create(NULL, "arm_gic");
qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
qdev_init_nofail(s->gic);
busdev = SYS_BUS_DEVICE(s->gic);
/* Pass through outbound IRQ lines from the GIC */
sysbus_pass_irq(dev, busdev);
/* Pass through inbound GPIO lines to the GIC */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_gic_set_irq,
EXYNOS4210_GIC_NIRQ - 32);
memory_region_init(&s->cpu_container, "exynos4210-cpu-container",
EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
memory_region_init(&s->dist_container, "exynos4210-dist-container",
EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
for (i = 0; i < s->num_cpu; i++) {
/* Map CPU interface per SMP Core */
sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
memory_region_init_alias(&s->cpu_alias[i],
cpu_alias_name,
sysbus_mmio_get_region(busdev, 1),
0,
EXYNOS4210_GIC_CPU_REGION_SIZE);
memory_region_add_subregion(&s->cpu_container,
EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
/* Map Distributor per SMP Core */
sprintf(dist_alias_name, "%s%x", dist_prefix, i);
memory_region_init_alias(&s->dist_alias[i],
dist_alias_name,
sysbus_mmio_get_region(busdev, 0),
0,
EXYNOS4210_GIC_DIST_REGION_SIZE);
memory_region_add_subregion(&s->dist_container,
EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
}
sysbus_init_mmio(dev, &s->cpu_container);
sysbus_init_mmio(dev, &s->dist_container);
return 0;
}
static Property exynos4210_gic_properties[] = {
DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
DEFINE_PROP_END_OF_LIST(),
};
static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_gic_init;
dc->props = exynos4210_gic_properties;
}
static const TypeInfo exynos4210_gic_info = {
.name = "exynos4210.gic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210GicState),
.class_init = exynos4210_gic_class_init,
};
static void exynos4210_gic_register_types(void)
{
type_register_static(&exynos4210_gic_info);
}
type_init(exynos4210_gic_register_types)
/* IRQ OR Gate struct.
*
* This device models an OR gate. There are n_in input qdev gpio lines and one
* output sysbus IRQ line. The output IRQ level is formed as OR between all
* gpio inputs.
*/
typedef struct {
SysBusDevice busdev;
uint32_t n_in; /* inputs amount */
uint32_t *level; /* input levels */
qemu_irq out; /* output IRQ */
} Exynos4210IRQGateState;
static Property exynos4210_irq_gate_properties[] = {
DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_exynos4210_irq_gate = {
.name = "exynos4210.irq_gate",
.version_id = 2,
.minimum_version_id = 2,
.minimum_version_id_old = 2,
.fields = (VMStateField[]) {
VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in),
VMSTATE_END_OF_LIST()
}
};
/* Process a change in IRQ input. */
static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
{
Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque;
uint32_t i;
assert(irq < s->n_in);
s->level[irq] = level;
for (i = 0; i < s->n_in; i++) {
if (s->level[i] >= 1) {
qemu_irq_raise(s->out);
return;
}
}
qemu_irq_lower(s->out);
}
static void exynos4210_irq_gate_reset(DeviceState *d)
{
Exynos4210IRQGateState *s =
DO_UPCAST(Exynos4210IRQGateState, busdev.qdev, d);
memset(s->level, 0, s->n_in * sizeof(*s->level));
}
/*
* IRQ Gate initialization.
*/
static int exynos4210_irq_gate_init(SysBusDevice *dev)
{
Exynos4210IRQGateState *s = FROM_SYSBUS(Exynos4210IRQGateState, dev);
/* Allocate general purpose input signals and connect a handler to each of
* them */
qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, s->n_in);
s->level = g_malloc0(s->n_in * sizeof(*s->level));
sysbus_init_irq(dev, &s->out);
return 0;
}
static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = exynos4210_irq_gate_init;
dc->reset = exynos4210_irq_gate_reset;
dc->vmsd = &vmstate_exynos4210_irq_gate;
dc->props = exynos4210_irq_gate_properties;
}
static const TypeInfo exynos4210_irq_gate_info = {
.name = "exynos4210.irq_gate",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210IRQGateState),
.class_init = exynos4210_irq_gate_class_init,
};
static void exynos4210_irq_gate_register_types(void)
{
type_register_static(&exynos4210_irq_gate_info);
}
type_init(exynos4210_irq_gate_register_types)

385
hw/intc/grlib_irqmp.c Normal file
View file

@ -0,0 +1,385 @@
/*
* QEMU GRLIB IRQMP Emulator
*
* (Multiprocessor and extended interrupt not supported)
*
* Copyright (c) 2010-2011 AdaCore
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/sysbus.h"
#include "cpu.h"
#include "hw/sparc/grlib.h"
#include "trace.h"
#define IRQMP_MAX_CPU 16
#define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */
/* Memory mapped register offsets */
#define LEVEL_OFFSET 0x00
#define PENDING_OFFSET 0x04
#define FORCE0_OFFSET 0x08
#define CLEAR_OFFSET 0x0C
#define MP_STATUS_OFFSET 0x10
#define BROADCAST_OFFSET 0x14
#define MASK_OFFSET 0x40
#define FORCE_OFFSET 0x80
#define EXTENDED_OFFSET 0xC0
typedef struct IRQMPState IRQMPState;
typedef struct IRQMP {
SysBusDevice busdev;
MemoryRegion iomem;
void *set_pil_in;
void *set_pil_in_opaque;
IRQMPState *state;
} IRQMP;
struct IRQMPState {
uint32_t level;
uint32_t pending;
uint32_t clear;
uint32_t broadcast;
uint32_t mask[IRQMP_MAX_CPU];
uint32_t force[IRQMP_MAX_CPU];
uint32_t extended[IRQMP_MAX_CPU];
IRQMP *parent;
};
static void grlib_irqmp_check_irqs(IRQMPState *state)
{
uint32_t pend = 0;
uint32_t level0 = 0;
uint32_t level1 = 0;
set_pil_in_fn set_pil_in;
assert(state != NULL);
assert(state->parent != NULL);
/* IRQ for CPU 0 (no SMP support) */
pend = (state->pending | state->force[0])
& state->mask[0];
level0 = pend & ~state->level;
level1 = pend & state->level;
trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
state->mask[0], level1, level0);
set_pil_in = (set_pil_in_fn)state->parent->set_pil_in;
/* Trigger level1 interrupt first and level0 if there is no level1 */
if (level1 != 0) {
set_pil_in(state->parent->set_pil_in_opaque, level1);
} else {
set_pil_in(state->parent->set_pil_in_opaque, level0);
}
}
void grlib_irqmp_ack(DeviceState *dev, int intno)
{
SysBusDevice *sdev;
IRQMP *irqmp;
IRQMPState *state;
uint32_t mask;
assert(dev != NULL);
sdev = SYS_BUS_DEVICE(dev);
assert(sdev != NULL);
irqmp = FROM_SYSBUS(typeof(*irqmp), sdev);
assert(irqmp != NULL);
state = irqmp->state;
assert(state != NULL);
intno &= 15;
mask = 1 << intno;
trace_grlib_irqmp_ack(intno);
/* Clear registers */
state->pending &= ~mask;
state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
grlib_irqmp_check_irqs(state);
}
void grlib_irqmp_set_irq(void *opaque, int irq, int level)
{
IRQMP *irqmp;
IRQMPState *s;
int i = 0;
assert(opaque != NULL);
irqmp = FROM_SYSBUS(typeof(*irqmp), SYS_BUS_DEVICE(opaque));
assert(irqmp != NULL);
s = irqmp->state;
assert(s != NULL);
assert(s->parent != NULL);
if (level) {
trace_grlib_irqmp_set_irq(irq);
if (s->broadcast & 1 << irq) {
/* Broadcasted IRQ */
for (i = 0; i < IRQMP_MAX_CPU; i++) {
s->force[i] |= 1 << irq;
}
} else {
s->pending |= 1 << irq;
}
grlib_irqmp_check_irqs(s);
}
}
static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
unsigned size)
{
IRQMP *irqmp = opaque;
IRQMPState *state;
assert(irqmp != NULL);
state = irqmp->state;
assert(state != NULL);
addr &= 0xff;
/* global registers */
switch (addr) {
case LEVEL_OFFSET:
return state->level;
case PENDING_OFFSET:
return state->pending;
case FORCE0_OFFSET:
/* This register is an "alias" for the force register of CPU 0 */
return state->force[0];
case CLEAR_OFFSET:
case MP_STATUS_OFFSET:
/* Always read as 0 */
return 0;
case BROADCAST_OFFSET:
return state->broadcast;
default:
break;
}
/* mask registers */
if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
int cpu = (addr - MASK_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
return state->mask[cpu];
}
/* force registers */
if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
int cpu = (addr - FORCE_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
return state->force[cpu];
}
/* extended (not supported) */
if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
int cpu = (addr - EXTENDED_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
return state->extended[cpu];
}
trace_grlib_irqmp_readl_unknown(addr);
return 0;
}
static void grlib_irqmp_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
IRQMP *irqmp = opaque;
IRQMPState *state;
assert(irqmp != NULL);
state = irqmp->state;
assert(state != NULL);
addr &= 0xff;
/* global registers */
switch (addr) {
case LEVEL_OFFSET:
value &= 0xFFFF << 1; /* clean up the value */
state->level = value;
return;
case PENDING_OFFSET:
/* Read Only */
return;
case FORCE0_OFFSET:
/* This register is an "alias" for the force register of CPU 0 */
value &= 0xFFFE; /* clean up the value */
state->force[0] = value;
grlib_irqmp_check_irqs(irqmp->state);
return;
case CLEAR_OFFSET:
value &= ~1; /* clean up the value */
state->pending &= ~value;
return;
case MP_STATUS_OFFSET:
/* Read Only (no SMP support) */
return;
case BROADCAST_OFFSET:
value &= 0xFFFE; /* clean up the value */
state->broadcast = value;
return;
default:
break;
}
/* mask registers */
if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
int cpu = (addr - MASK_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
value &= ~1; /* clean up the value */
state->mask[cpu] = value;
grlib_irqmp_check_irqs(irqmp->state);
return;
}
/* force registers */
if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
int cpu = (addr - FORCE_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
uint32_t force = value & 0xFFFE;
uint32_t clear = (value >> 16) & 0xFFFE;
uint32_t old = state->force[cpu];
state->force[cpu] = (old | force) & ~clear;
grlib_irqmp_check_irqs(irqmp->state);
return;
}
/* extended (not supported) */
if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
int cpu = (addr - EXTENDED_OFFSET) / 4;
assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
value &= 0xF; /* clean up the value */
state->extended[cpu] = value;
return;
}
trace_grlib_irqmp_writel_unknown(addr, value);
}
static const MemoryRegionOps grlib_irqmp_ops = {
.read = grlib_irqmp_read,
.write = grlib_irqmp_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void grlib_irqmp_reset(DeviceState *d)
{
IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev);
assert(irqmp != NULL);
assert(irqmp->state != NULL);
memset(irqmp->state, 0, sizeof *irqmp->state);
irqmp->state->parent = irqmp;
}
static int grlib_irqmp_init(SysBusDevice *dev)
{
IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev);
assert(irqmp != NULL);
/* Check parameters */
if (irqmp->set_pil_in == NULL) {
return -1;
}
memory_region_init_io(&irqmp->iomem, &grlib_irqmp_ops, irqmp,
"irqmp", IRQMP_REG_SIZE);
irqmp->state = g_malloc0(sizeof *irqmp->state);
sysbus_init_mmio(dev, &irqmp->iomem);
return 0;
}
static Property grlib_irqmp_properties[] = {
DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in),
DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque),
DEFINE_PROP_END_OF_LIST(),
};
static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = grlib_irqmp_init;
dc->reset = grlib_irqmp_reset;
dc->props = grlib_irqmp_properties;
}
static const TypeInfo grlib_irqmp_info = {
.name = "grlib,irqmp",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IRQMP),
.class_init = grlib_irqmp_class_init,
};
static void grlib_irqmp_register_types(void)
{
type_register_static(&grlib_irqmp_info);
}
type_init(grlib_irqmp_register_types)

408
hw/intc/imx_avic.c Normal file
View file

@ -0,0 +1,408 @@
/*
* i.MX31 Vectored Interrupt Controller
*
* Note this is NOT the PL192 provided by ARM, but
* a custom implementation by Freescale.
*
* Copyright (c) 2008 OKL
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
* TODO: implement vectors.
*/
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "qemu/host-utils.h"
#define DEBUG_INT 1
#undef DEBUG_INT /* comment out for debugging */
#ifdef DEBUG_INT
#define DPRINTF(fmt, args...) \
do { printf("imx_avic: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do {} while (0)
#endif
/*
* Define to 1 for messages about attempts to
* access unimplemented registers or similar.
*/
#define DEBUG_IMPLEMENTATION 1
#if DEBUG_IMPLEMENTATION
# define IPRINTF(fmt, args...) \
do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0)
#else
# define IPRINTF(fmt, args...) do {} while (0)
#endif
#define IMX_AVIC_NUM_IRQS 64
/* Interrupt Control Bits */
#define ABFLAG (1<<25)
#define ABFEN (1<<24)
#define NIDIS (1<<22) /* Normal Interrupt disable */
#define FIDIS (1<<21) /* Fast interrupt disable */
#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */
#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */
#define NM (1<<18) /* Normal interrupt mode */
#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4)
#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
uint64_t pending;
uint64_t enabled;
uint64_t is_fiq;
uint32_t intcntl;
uint32_t intmask;
qemu_irq irq;
qemu_irq fiq;
uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
} IMXAVICState;
static const VMStateDescription vmstate_imx_avic = {
.name = "imx-avic",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(pending, IMXAVICState),
VMSTATE_UINT64(enabled, IMXAVICState),
VMSTATE_UINT64(is_fiq, IMXAVICState),
VMSTATE_UINT32(intcntl, IMXAVICState),
VMSTATE_UINT32(intmask, IMXAVICState),
VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS),
VMSTATE_END_OF_LIST()
},
};
static inline int imx_avic_prio(IMXAVICState *s, int irq)
{
uint32_t word = irq / PRIO_PER_WORD;
uint32_t part = 4 * (irq % PRIO_PER_WORD);
return 0xf & (s->prio[word] >> part);
}
static inline void imx_avic_set_prio(IMXAVICState *s, int irq, int prio)
{
uint32_t word = irq / PRIO_PER_WORD;
uint32_t part = 4 * (irq % PRIO_PER_WORD);
uint32_t mask = ~(0xf << part);
s->prio[word] &= mask;
s->prio[word] |= prio << part;
}
/* Update interrupts. */
static void imx_avic_update(IMXAVICState *s)
{
int i;
uint64_t new = s->pending & s->enabled;
uint64_t flags;
flags = new & s->is_fiq;
qemu_set_irq(s->fiq, !!flags);
flags = new & ~s->is_fiq;
if (!flags || (s->intmask == 0x1f)) {
qemu_set_irq(s->irq, !!flags);
return;
}
/*
* Take interrupt if there's a pending interrupt with
* priority higher than the value of intmask
*/
for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) {
if (flags & (1UL << i)) {
if (imx_avic_prio(s, i) > s->intmask) {
qemu_set_irq(s->irq, 1);
return;
}
}
}
qemu_set_irq(s->irq, 0);
}
static void imx_avic_set_irq(void *opaque, int irq, int level)
{
IMXAVICState *s = (IMXAVICState *)opaque;
if (level) {
DPRINTF("Raising IRQ %d, prio %d\n",
irq, imx_avic_prio(s, irq));
s->pending |= (1ULL << irq);
} else {
DPRINTF("Clearing IRQ %d, prio %d\n",
irq, imx_avic_prio(s, irq));
s->pending &= ~(1ULL << irq);
}
imx_avic_update(s);
}
static uint64_t imx_avic_read(void *opaque,
hwaddr offset, unsigned size)
{
IMXAVICState *s = (IMXAVICState *)opaque;
DPRINTF("read(offset = 0x%x)\n", offset >> 2);
switch (offset >> 2) {
case 0: /* INTCNTL */
return s->intcntl;
case 1: /* Normal Interrupt Mask Register, NIMASK */
return s->intmask;
case 2: /* Interrupt Enable Number Register, INTENNUM */
case 3: /* Interrupt Disable Number Register, INTDISNUM */
return 0;
case 4: /* Interrupt Enabled Number Register High */
return s->enabled >> 32;
case 5: /* Interrupt Enabled Number Register Low */
return s->enabled & 0xffffffffULL;
case 6: /* Interrupt Type Register High */
return s->is_fiq >> 32;
case 7: /* Interrupt Type Register Low */
return s->is_fiq & 0xffffffffULL;
case 8: /* Normal Interrupt Priority Register 7 */
case 9: /* Normal Interrupt Priority Register 6 */
case 10:/* Normal Interrupt Priority Register 5 */
case 11:/* Normal Interrupt Priority Register 4 */
case 12:/* Normal Interrupt Priority Register 3 */
case 13:/* Normal Interrupt Priority Register 2 */
case 14:/* Normal Interrupt Priority Register 1 */
case 15:/* Normal Interrupt Priority Register 0 */
return s->prio[15-(offset>>2)];
case 16: /* Normal interrupt vector and status register */
{
/*
* This returns the highest priority
* outstanding interrupt. Where there is more than
* one pending IRQ with the same priority,
* take the highest numbered one.
*/
uint64_t flags = s->pending & s->enabled & ~s->is_fiq;
int i;
int prio = -1;
int irq = -1;
for (i = 63; i >= 0; --i) {
if (flags & (1ULL<<i)) {
int irq_prio = imx_avic_prio(s, i);
if (irq_prio > prio) {
irq = i;
prio = irq_prio;
}
}
}
if (irq >= 0) {
imx_avic_set_irq(s, irq, 0);
return irq << 16 | prio;
}
return 0xffffffffULL;
}
case 17:/* Fast Interrupt vector and status register */
{
uint64_t flags = s->pending & s->enabled & s->is_fiq;
int i = ctz64(flags);
if (i < 64) {
imx_avic_set_irq(opaque, i, 0);
return i;
}
return 0xffffffffULL;
}
case 18:/* Interrupt source register high */
return s->pending >> 32;
case 19:/* Interrupt source register low */
return s->pending & 0xffffffffULL;
case 20:/* Interrupt Force Register high */
case 21:/* Interrupt Force Register low */
return 0;
case 22:/* Normal Interrupt Pending Register High */
return (s->pending & s->enabled & ~s->is_fiq) >> 32;
case 23:/* Normal Interrupt Pending Register Low */
return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL;
case 24: /* Fast Interrupt Pending Register High */
return (s->pending & s->enabled & s->is_fiq) >> 32;
case 25: /* Fast Interrupt Pending Register Low */
return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL;
case 0x40: /* AVIC vector 0, use for WFI WAR */
return 0x4;
default:
IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset);
return 0;
}
}
static void imx_avic_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
IMXAVICState *s = (IMXAVICState *)opaque;
/* Vector Registers not yet supported */
if (offset >= 0x100 && offset <= 0x2fc) {
IPRINTF("imx_avic_write to vector register %d ignored\n",
(unsigned int)((offset - 0x100) >> 2));
return;
}
DPRINTF("imx_avic_write(0x%x) = %x\n",
(unsigned int)offset>>2, (unsigned int)val);
switch (offset >> 2) {
case 0: /* Interrupt Control Register, INTCNTL */
s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
if (s->intcntl & ABFEN) {
s->intcntl &= ~(val & ABFLAG);
}
break;
case 1: /* Normal Interrupt Mask Register, NIMASK */
s->intmask = val & 0x1f;
break;
case 2: /* Interrupt Enable Number Register, INTENNUM */
DPRINTF("enable(%d)\n", (int)val);
val &= 0x3f;
s->enabled |= (1ULL << val);
break;
case 3: /* Interrupt Disable Number Register, INTDISNUM */
DPRINTF("disable(%d)\n", (int)val);
val &= 0x3f;
s->enabled &= ~(1ULL << val);
break;
case 4: /* Interrupt Enable Number Register High */
s->enabled = (s->enabled & 0xffffffffULL) | (val << 32);
break;
case 5: /* Interrupt Enable Number Register Low */
s->enabled = (s->enabled & 0xffffffff00000000ULL) | val;
break;
case 6: /* Interrupt Type Register High */
s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32);
break;
case 7: /* Interrupt Type Register Low */
s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val;
break;
case 8: /* Normal Interrupt Priority Register 7 */
case 9: /* Normal Interrupt Priority Register 6 */
case 10:/* Normal Interrupt Priority Register 5 */
case 11:/* Normal Interrupt Priority Register 4 */
case 12:/* Normal Interrupt Priority Register 3 */
case 13:/* Normal Interrupt Priority Register 2 */
case 14:/* Normal Interrupt Priority Register 1 */
case 15:/* Normal Interrupt Priority Register 0 */
s->prio[15-(offset>>2)] = val;
break;
/* Read-only registers, writes ignored */
case 16:/* Normal Interrupt Vector and Status register */
case 17:/* Fast Interrupt vector and status register */
case 18:/* Interrupt source register high */
case 19:/* Interrupt source register low */
return;
case 20:/* Interrupt Force Register high */
s->pending = (s->pending & 0xffffffffULL) | (val << 32);
break;
case 21:/* Interrupt Force Register low */
s->pending = (s->pending & 0xffffffff00000000ULL) | val;
break;
case 22:/* Normal Interrupt Pending Register High */
case 23:/* Normal Interrupt Pending Register Low */
case 24: /* Fast Interrupt Pending Register High */
case 25: /* Fast Interrupt Pending Register Low */
return;
default:
IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset);
}
imx_avic_update(s);
}
static const MemoryRegionOps imx_avic_ops = {
.read = imx_avic_read,
.write = imx_avic_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void imx_avic_reset(DeviceState *dev)
{
IMXAVICState *s = container_of(dev, IMXAVICState, busdev.qdev);
s->pending = 0;
s->enabled = 0;
s->is_fiq = 0;
s->intmask = 0x1f;
s->intcntl = 0;
memset(s->prio, 0, sizeof s->prio);
}
static int imx_avic_init(SysBusDevice *dev)
{
IMXAVICState *s = FROM_SYSBUS(IMXAVICState, dev);;
memory_region_init_io(&s->iomem, &imx_avic_ops, s, "imx_avic", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
qdev_init_gpio_in(&dev->qdev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
sysbus_init_irq(dev, &s->irq);
sysbus_init_irq(dev, &s->fiq);
return 0;
}
static void imx_avic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = imx_avic_init;
dc->vmsd = &vmstate_imx_avic;
dc->reset = imx_avic_reset;
dc->desc = "i.MX Advanced Vector Interrupt Controller";
}
static const TypeInfo imx_avic_info = {
.name = "imx_avic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXAVICState),
.class_init = imx_avic_class_init,
};
static void imx_avic_register_types(void)
{
type_register_static(&imx_avic_info);
}
type_init(imx_avic_register_types)

258
hw/intc/ioapic.c Normal file
View file

@ -0,0 +1,258 @@
/*
* ioapic.c IOAPIC emulation logic
*
* Copyright (c) 2004-2005 Fabrice Bellard
*
* Split the ioapic logic from apic.c
* Xiantao Zhang <xiantao.zhang@intel.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 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/>.
*/
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
//#define DEBUG_IOAPIC
#ifdef DEBUG_IOAPIC
#define DPRINTF(fmt, ...) \
do { printf("ioapic: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...)
#endif
static IOAPICCommonState *ioapics[MAX_IOAPICS];
static void ioapic_service(IOAPICCommonState *s)
{
uint8_t i;
uint8_t trig_mode;
uint8_t vector;
uint8_t delivery_mode;
uint32_t mask;
uint64_t entry;
uint8_t dest;
uint8_t dest_mode;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
mask = 1 << i;
if (s->irr & mask) {
entry = s->ioredtbl[i];
if (!(entry & IOAPIC_LVT_MASKED)) {
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
dest = entry >> IOAPIC_LVT_DEST_SHIFT;
dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
delivery_mode =
(entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
s->irr &= ~mask;
} else {
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
}
if (delivery_mode == IOAPIC_DM_EXTINT) {
vector = pic_read_irq(isa_pic);
} else {
vector = entry & IOAPIC_VECTOR_MASK;
}
apic_deliver_irq(dest, dest_mode, delivery_mode,
vector, trig_mode);
}
}
}
}
static void ioapic_set_irq(void *opaque, int vector, int level)
{
IOAPICCommonState *s = opaque;
/* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
* to GSI 2. GSI maps to ioapic 1-1. This is not
* the cleanest way of doing it but it should work. */
DPRINTF("%s: %s vec %x\n", __func__, level ? "raise" : "lower", vector);
if (vector == 0) {
vector = 2;
}
if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
uint32_t mask = 1 << vector;
uint64_t entry = s->ioredtbl[vector];
if (entry & (1 << IOAPIC_LVT_POLARITY_SHIFT)) {
level = !level;
}
if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) ==
IOAPIC_TRIGGER_LEVEL) {
/* level triggered */
if (level) {
s->irr |= mask;
ioapic_service(s);
} else {
s->irr &= ~mask;
}
} else {
/* According to the 82093AA manual, we must ignore edge requests
* if the input pin is masked. */
if (level && !(entry & IOAPIC_LVT_MASKED)) {
s->irr |= mask;
ioapic_service(s);
}
}
}
}
void ioapic_eoi_broadcast(int vector)
{
IOAPICCommonState *s;
uint64_t entry;
int i, n;
for (i = 0; i < MAX_IOAPICS; i++) {
s = ioapics[i];
if (!s) {
continue;
}
for (n = 0; n < IOAPIC_NUM_PINS; n++) {
entry = s->ioredtbl[n];
if ((entry & IOAPIC_LVT_REMOTE_IRR)
&& (entry & IOAPIC_VECTOR_MASK) == vector) {
s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR;
if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) {
ioapic_service(s);
}
}
}
}
}
static uint64_t
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
{
IOAPICCommonState *s = opaque;
int index;
uint32_t val = 0;
switch (addr & 0xff) {
case IOAPIC_IOREGSEL:
val = s->ioregsel;
break;
case IOAPIC_IOWIN:
if (size != 4) {
break;
}
switch (s->ioregsel) {
case IOAPIC_REG_ID:
val = s->id << IOAPIC_ID_SHIFT;
break;
case IOAPIC_REG_VER:
val = IOAPIC_VERSION |
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
break;
case IOAPIC_REG_ARB:
val = 0;
break;
default:
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
if (index >= 0 && index < IOAPIC_NUM_PINS) {
if (s->ioregsel & 1) {
val = s->ioredtbl[index] >> 32;
} else {
val = s->ioredtbl[index] & 0xffffffff;
}
}
}
DPRINTF("read: %08x = %08x\n", s->ioregsel, val);
break;
}
return val;
}
static void
ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
IOAPICCommonState *s = opaque;
int index;
switch (addr & 0xff) {
case IOAPIC_IOREGSEL:
s->ioregsel = val;
break;
case IOAPIC_IOWIN:
if (size != 4) {
break;
}
DPRINTF("write: %08x = %08" PRIx64 "\n", s->ioregsel, val);
switch (s->ioregsel) {
case IOAPIC_REG_ID:
s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK;
break;
case IOAPIC_REG_VER:
case IOAPIC_REG_ARB:
break;
default:
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
if (index >= 0 && index < IOAPIC_NUM_PINS) {
if (s->ioregsel & 1) {
s->ioredtbl[index] &= 0xffffffff;
s->ioredtbl[index] |= (uint64_t)val << 32;
} else {
s->ioredtbl[index] &= ~0xffffffffULL;
s->ioredtbl[index] |= val;
}
ioapic_service(s);
}
}
break;
}
}
static const MemoryRegionOps ioapic_io_ops = {
.read = ioapic_mem_read,
.write = ioapic_mem_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ioapic_init(IOAPICCommonState *s, int instance_no)
{
memory_region_init_io(&s->io_memory, &ioapic_io_ops, s, "ioapic", 0x1000);
qdev_init_gpio_in(&s->busdev.qdev, ioapic_set_irq, IOAPIC_NUM_PINS);
ioapics[instance_no] = s;
}
static void ioapic_class_init(ObjectClass *klass, void *data)
{
IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->init = ioapic_init;
dc->reset = ioapic_reset_common;
}
static const TypeInfo ioapic_info = {
.name = "ioapic",
.parent = TYPE_IOAPIC_COMMON,
.instance_size = sizeof(IOAPICCommonState),
.class_init = ioapic_class_init,
};
static void ioapic_register_types(void)
{
type_register_static(&ioapic_info);
}
type_init(ioapic_register_types)

120
hw/intc/ioapic_common.c Normal file
View file

@ -0,0 +1,120 @@
/*
* IOAPIC emulation logic - common bits of emulated and KVM kernel model
*
* Copyright (c) 2004-2005 Fabrice Bellard
* Copyright (c) 2009 Xiantao Zhang, Intel
* Copyright (c) 2011 Jan Kiszka, Siemens AG
*
* 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 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/>.
*/
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/sysbus.h"
void ioapic_reset_common(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
int i;
s->id = 0;
s->ioregsel = 0;
s->irr = 0;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
s->ioredtbl[i] = 1 << IOAPIC_LVT_MASKED_SHIFT;
}
}
static void ioapic_dispatch_pre_save(void *opaque)
{
IOAPICCommonState *s = IOAPIC_COMMON(opaque);
IOAPICCommonClass *info = IOAPIC_COMMON_GET_CLASS(s);
if (info->pre_save) {
info->pre_save(s);
}
}
static int ioapic_dispatch_post_load(void *opaque, int version_id)
{
IOAPICCommonState *s = IOAPIC_COMMON(opaque);
IOAPICCommonClass *info = IOAPIC_COMMON_GET_CLASS(s);
if (info->post_load) {
info->post_load(s);
}
return 0;
}
static int ioapic_init_common(SysBusDevice *dev)
{
IOAPICCommonState *s = FROM_SYSBUS(IOAPICCommonState, dev);
IOAPICCommonClass *info;
static int ioapic_no;
if (ioapic_no >= MAX_IOAPICS) {
return -1;
}
info = IOAPIC_COMMON_GET_CLASS(s);
info->init(s, ioapic_no);
sysbus_init_mmio(&s->busdev, &s->io_memory);
ioapic_no++;
return 0;
}
static const VMStateDescription vmstate_ioapic_common = {
.name = "ioapic",
.version_id = 3,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = ioapic_dispatch_pre_save,
.post_load = ioapic_dispatch_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8(id, IOAPICCommonState),
VMSTATE_UINT8(ioregsel, IOAPICCommonState),
VMSTATE_UNUSED_V(2, 8), /* to account for qemu-kvm's v2 format */
VMSTATE_UINT32_V(irr, IOAPICCommonState, 2),
VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICCommonState, IOAPIC_NUM_PINS),
VMSTATE_END_OF_LIST()
}
};
static void ioapic_common_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
sc->init = ioapic_init_common;
dc->vmsd = &vmstate_ioapic_common;
dc->no_user = 1;
}
static const TypeInfo ioapic_common_type = {
.name = TYPE_IOAPIC_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IOAPICCommonState),
.class_size = sizeof(IOAPICCommonClass),
.class_init = ioapic_common_class_init,
.abstract = true,
};
static void register_types(void)
{
type_register_static(&ioapic_common_type);
}
type_init(register_types)

199
hw/intc/lm32_pic.c Normal file
View file

@ -0,0 +1,199 @@
/*
* LatticeMico32 CPU interrupt controller logic.
*
* Copyright (c) 2010 Michael Walle <michael@walle.cc>
*
* 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 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/>.
*/
#include <assert.h>
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
#include "trace.h"
#include "hw/lm32/lm32_pic.h"
struct LM32PicState {
SysBusDevice busdev;
qemu_irq parent_irq;
uint32_t im; /* interrupt mask */
uint32_t ip; /* interrupt pending */
uint32_t irq_state;
/* statistics */
uint32_t stats_irq_count[32];
};
typedef struct LM32PicState LM32PicState;
static LM32PicState *pic;
void lm32_do_pic_info(Monitor *mon, const QDict *qdict)
{
if (pic == NULL) {
return;
}
monitor_printf(mon, "lm32-pic: im=%08x ip=%08x irq_state=%08x\n",
pic->im, pic->ip, pic->irq_state);
}
void lm32_irq_info(Monitor *mon, const QDict *qdict)
{
int i;
uint32_t count;
if (pic == NULL) {
return;
}
monitor_printf(mon, "IRQ statistics:\n");
for (i = 0; i < 32; i++) {
count = pic->stats_irq_count[i];
if (count > 0) {
monitor_printf(mon, "%2d: %u\n", i, count);
}
}
}
static void update_irq(LM32PicState *s)
{
s->ip |= s->irq_state;
if (s->ip & s->im) {
trace_lm32_pic_raise_irq();
qemu_irq_raise(s->parent_irq);
} else {
trace_lm32_pic_lower_irq();
qemu_irq_lower(s->parent_irq);
}
}
static void irq_handler(void *opaque, int irq, int level)
{
LM32PicState *s = opaque;
assert(irq < 32);
trace_lm32_pic_interrupt(irq, level);
if (level) {
s->irq_state |= (1 << irq);
s->stats_irq_count[irq]++;
} else {
s->irq_state &= ~(1 << irq);
}
update_irq(s);
}
void lm32_pic_set_im(DeviceState *d, uint32_t im)
{
LM32PicState *s = container_of(d, LM32PicState, busdev.qdev);
trace_lm32_pic_set_im(im);
s->im = im;
update_irq(s);
}
void lm32_pic_set_ip(DeviceState *d, uint32_t ip)
{
LM32PicState *s = container_of(d, LM32PicState, busdev.qdev);
trace_lm32_pic_set_ip(ip);
/* ack interrupt */
s->ip &= ~ip;
update_irq(s);
}
uint32_t lm32_pic_get_im(DeviceState *d)
{
LM32PicState *s = container_of(d, LM32PicState, busdev.qdev);
trace_lm32_pic_get_im(s->im);
return s->im;
}
uint32_t lm32_pic_get_ip(DeviceState *d)
{
LM32PicState *s = container_of(d, LM32PicState, busdev.qdev);
trace_lm32_pic_get_ip(s->ip);
return s->ip;
}
static void pic_reset(DeviceState *d)
{
LM32PicState *s = container_of(d, LM32PicState, busdev.qdev);
int i;
s->im = 0;
s->ip = 0;
s->irq_state = 0;
for (i = 0; i < 32; i++) {
s->stats_irq_count[i] = 0;
}
}
static int lm32_pic_init(SysBusDevice *dev)
{
LM32PicState *s = FROM_SYSBUS(typeof(*s), dev);
qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
sysbus_init_irq(dev, &s->parent_irq);
pic = s;
return 0;
}
static const VMStateDescription vmstate_lm32_pic = {
.name = "lm32-pic",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(im, LM32PicState),
VMSTATE_UINT32(ip, LM32PicState),
VMSTATE_UINT32(irq_state, LM32PicState),
VMSTATE_UINT32_ARRAY(stats_irq_count, LM32PicState, 32),
VMSTATE_END_OF_LIST()
}
};
static void lm32_pic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = lm32_pic_init;
dc->reset = pic_reset;
dc->vmsd = &vmstate_lm32_pic;
}
static const TypeInfo lm32_pic_info = {
.name = "lm32-pic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(LM32PicState),
.class_init = lm32_pic_class_init,
};
static void lm32_pic_register_types(void)
{
type_register_static(&lm32_pic_info);
}
type_init(lm32_pic_register_types)

649
hw/intc/omap_intc.c Normal file
View file

@ -0,0 +1,649 @@
/*
* TI OMAP interrupt controller emulation.
*
* Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
* Copyright (C) 2007-2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/hw.h"
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
/* Interrupt Handlers */
struct omap_intr_handler_bank_s {
uint32_t irqs;
uint32_t inputs;
uint32_t mask;
uint32_t fiq;
uint32_t sens_edge;
uint32_t swi;
unsigned char priority[32];
};
struct omap_intr_handler_s {
SysBusDevice busdev;
qemu_irq *pins;
qemu_irq parent_intr[2];
MemoryRegion mmio;
void *iclk;
void *fclk;
unsigned char nbanks;
int level_only;
uint32_t size;
uint8_t revision;
/* state */
uint32_t new_agr[2];
int sir_intr[2];
int autoidle;
uint32_t mask;
struct omap_intr_handler_bank_s bank[3];
};
static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
{
int i, j, sir_intr, p_intr, p, f;
uint32_t level;
sir_intr = 0;
p_intr = 255;
/* Find the interrupt line with the highest dynamic priority.
* Note: 0 denotes the hightest priority.
* If all interrupts have the same priority, the default order is IRQ_N,
* IRQ_N-1,...,IRQ_0. */
for (j = 0; j < s->nbanks; ++j) {
level = s->bank[j].irqs & ~s->bank[j].mask &
(is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f,
level >>= f) {
p = s->bank[j].priority[i];
if (p <= p_intr) {
p_intr = p;
sir_intr = 32 * j + i;
}
f = ffs(level >> 1);
}
}
s->sir_intr[is_fiq] = sir_intr;
}
static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq)
{
int i;
uint32_t has_intr = 0;
for (i = 0; i < s->nbanks; ++i)
has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
(is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
if (s->new_agr[is_fiq] & has_intr & s->mask) {
s->new_agr[is_fiq] = 0;
omap_inth_sir_update(s, is_fiq);
qemu_set_irq(s->parent_intr[is_fiq], 1);
}
}
#define INT_FALLING_EDGE 0
#define INT_LOW_LEVEL 1
static void omap_set_intr(void *opaque, int irq, int req)
{
struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
uint32_t rise;
struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
int n = irq & 31;
if (req) {
rise = ~bank->irqs & (1 << n);
if (~bank->sens_edge & (1 << n))
rise &= ~bank->inputs;
bank->inputs |= (1 << n);
if (rise) {
bank->irqs |= rise;
omap_inth_update(ih, 0);
omap_inth_update(ih, 1);
}
} else {
rise = bank->sens_edge & bank->irqs & (1 << n);
bank->irqs &= ~rise;
bank->inputs &= ~(1 << n);
}
}
/* Simplified version with no edge detection */
static void omap_set_intr_noedge(void *opaque, int irq, int req)
{
struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
uint32_t rise;
struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
int n = irq & 31;
if (req) {
rise = ~bank->inputs & (1 << n);
if (rise) {
bank->irqs |= bank->inputs |= rise;
omap_inth_update(ih, 0);
omap_inth_update(ih, 1);
}
} else
bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi;
}
static uint64_t omap_inth_read(void *opaque, hwaddr addr,
unsigned size)
{
struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
int i, offset = addr;
int bank_no = offset >> 8;
int line_no;
struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
offset &= 0xff;
switch (offset) {
case 0x00: /* ITR */
return bank->irqs;
case 0x04: /* MIR */
return bank->mask;
case 0x10: /* SIR_IRQ_CODE */
case 0x14: /* SIR_FIQ_CODE */
if (bank_no != 0)
break;
line_no = s->sir_intr[(offset - 0x10) >> 2];
bank = &s->bank[line_no >> 5];
i = line_no & 31;
if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
bank->irqs &= ~(1 << i);
return line_no;
case 0x18: /* CONTROL_REG */
if (bank_no != 0)
break;
return 0;
case 0x1c: /* ILR0 */
case 0x20: /* ILR1 */
case 0x24: /* ILR2 */
case 0x28: /* ILR3 */
case 0x2c: /* ILR4 */
case 0x30: /* ILR5 */
case 0x34: /* ILR6 */
case 0x38: /* ILR7 */
case 0x3c: /* ILR8 */
case 0x40: /* ILR9 */
case 0x44: /* ILR10 */
case 0x48: /* ILR11 */
case 0x4c: /* ILR12 */
case 0x50: /* ILR13 */
case 0x54: /* ILR14 */
case 0x58: /* ILR15 */
case 0x5c: /* ILR16 */
case 0x60: /* ILR17 */
case 0x64: /* ILR18 */
case 0x68: /* ILR19 */
case 0x6c: /* ILR20 */
case 0x70: /* ILR21 */
case 0x74: /* ILR22 */
case 0x78: /* ILR23 */
case 0x7c: /* ILR24 */
case 0x80: /* ILR25 */
case 0x84: /* ILR26 */
case 0x88: /* ILR27 */
case 0x8c: /* ILR28 */
case 0x90: /* ILR29 */
case 0x94: /* ILR30 */
case 0x98: /* ILR31 */
i = (offset - 0x1c) >> 2;
return (bank->priority[i] << 2) |
(((bank->sens_edge >> i) & 1) << 1) |
((bank->fiq >> i) & 1);
case 0x9c: /* ISR */
return 0x00000000;
}
OMAP_BAD_REG(addr);
return 0;
}
static void omap_inth_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
int i, offset = addr;
int bank_no = offset >> 8;
struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
offset &= 0xff;
switch (offset) {
case 0x00: /* ITR */
/* Important: ignore the clearing if the IRQ is level-triggered and
the input bit is 1 */
bank->irqs &= value | (bank->inputs & bank->sens_edge);
return;
case 0x04: /* MIR */
bank->mask = value;
omap_inth_update(s, 0);
omap_inth_update(s, 1);
return;
case 0x10: /* SIR_IRQ_CODE */
case 0x14: /* SIR_FIQ_CODE */
OMAP_RO_REG(addr);
break;
case 0x18: /* CONTROL_REG */
if (bank_no != 0)
break;
if (value & 2) {
qemu_set_irq(s->parent_intr[1], 0);
s->new_agr[1] = ~0;
omap_inth_update(s, 1);
}
if (value & 1) {
qemu_set_irq(s->parent_intr[0], 0);
s->new_agr[0] = ~0;
omap_inth_update(s, 0);
}
return;
case 0x1c: /* ILR0 */
case 0x20: /* ILR1 */
case 0x24: /* ILR2 */
case 0x28: /* ILR3 */
case 0x2c: /* ILR4 */
case 0x30: /* ILR5 */
case 0x34: /* ILR6 */
case 0x38: /* ILR7 */
case 0x3c: /* ILR8 */
case 0x40: /* ILR9 */
case 0x44: /* ILR10 */
case 0x48: /* ILR11 */
case 0x4c: /* ILR12 */
case 0x50: /* ILR13 */
case 0x54: /* ILR14 */
case 0x58: /* ILR15 */
case 0x5c: /* ILR16 */
case 0x60: /* ILR17 */
case 0x64: /* ILR18 */
case 0x68: /* ILR19 */
case 0x6c: /* ILR20 */
case 0x70: /* ILR21 */
case 0x74: /* ILR22 */
case 0x78: /* ILR23 */
case 0x7c: /* ILR24 */
case 0x80: /* ILR25 */
case 0x84: /* ILR26 */
case 0x88: /* ILR27 */
case 0x8c: /* ILR28 */
case 0x90: /* ILR29 */
case 0x94: /* ILR30 */
case 0x98: /* ILR31 */
i = (offset - 0x1c) >> 2;
bank->priority[i] = (value >> 2) & 0x1f;
bank->sens_edge &= ~(1 << i);
bank->sens_edge |= ((value >> 1) & 1) << i;
bank->fiq &= ~(1 << i);
bank->fiq |= (value & 1) << i;
return;
case 0x9c: /* ISR */
for (i = 0; i < 32; i ++)
if (value & (1 << i)) {
omap_set_intr(s, 32 * bank_no + i, 1);
return;
}
return;
}
OMAP_BAD_REG(addr);
}
static const MemoryRegionOps omap_inth_mem_ops = {
.read = omap_inth_read,
.write = omap_inth_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void omap_inth_reset(DeviceState *dev)
{
struct omap_intr_handler_s *s = FROM_SYSBUS(struct omap_intr_handler_s,
SYS_BUS_DEVICE(dev));
int i;
for (i = 0; i < s->nbanks; ++i){
s->bank[i].irqs = 0x00000000;
s->bank[i].mask = 0xffffffff;
s->bank[i].sens_edge = 0x00000000;
s->bank[i].fiq = 0x00000000;
s->bank[i].inputs = 0x00000000;
s->bank[i].swi = 0x00000000;
memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
if (s->level_only)
s->bank[i].sens_edge = 0xffffffff;
}
s->new_agr[0] = ~0;
s->new_agr[1] = ~0;
s->sir_intr[0] = 0;
s->sir_intr[1] = 0;
s->autoidle = 0;
s->mask = ~0;
qemu_set_irq(s->parent_intr[0], 0);
qemu_set_irq(s->parent_intr[1], 0);
}
static int omap_intc_init(SysBusDevice *dev)
{
struct omap_intr_handler_s *s;
s = FROM_SYSBUS(struct omap_intr_handler_s, dev);
if (!s->iclk) {
hw_error("omap-intc: clk not connected\n");
}
s->nbanks = 1;
sysbus_init_irq(dev, &s->parent_intr[0]);
sysbus_init_irq(dev, &s->parent_intr[1]);
qdev_init_gpio_in(&dev->qdev, omap_set_intr, s->nbanks * 32);
memory_region_init_io(&s->mmio, &omap_inth_mem_ops, s,
"omap-intc", s->size);
sysbus_init_mmio(dev, &s->mmio);
return 0;
}
static Property omap_intc_properties[] = {
DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100),
DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk),
DEFINE_PROP_END_OF_LIST(),
};
static void omap_intc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = omap_intc_init;
dc->reset = omap_inth_reset;
dc->props = omap_intc_properties;
}
static const TypeInfo omap_intc_info = {
.name = "omap-intc",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct omap_intr_handler_s),
.class_init = omap_intc_class_init,
};
static uint64_t omap2_inth_read(void *opaque, hwaddr addr,
unsigned size)
{
struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
int offset = addr;
int bank_no, line_no;
struct omap_intr_handler_bank_s *bank = NULL;
if ((offset & 0xf80) == 0x80) {
bank_no = (offset & 0x60) >> 5;
if (bank_no < s->nbanks) {
offset &= ~0x60;
bank = &s->bank[bank_no];
} else {
OMAP_BAD_REG(addr);
return 0;
}
}
switch (offset) {
case 0x00: /* INTC_REVISION */
return s->revision;
case 0x10: /* INTC_SYSCONFIG */
return (s->autoidle >> 2) & 1;
case 0x14: /* INTC_SYSSTATUS */
return 1; /* RESETDONE */
case 0x40: /* INTC_SIR_IRQ */
return s->sir_intr[0];
case 0x44: /* INTC_SIR_FIQ */
return s->sir_intr[1];
case 0x48: /* INTC_CONTROL */
return (!s->mask) << 2; /* GLOBALMASK */
case 0x4c: /* INTC_PROTECTION */
return 0;
case 0x50: /* INTC_IDLE */
return s->autoidle & 3;
/* Per-bank registers */
case 0x80: /* INTC_ITR */
return bank->inputs;
case 0x84: /* INTC_MIR */
return bank->mask;
case 0x88: /* INTC_MIR_CLEAR */
case 0x8c: /* INTC_MIR_SET */
return 0;
case 0x90: /* INTC_ISR_SET */
return bank->swi;
case 0x94: /* INTC_ISR_CLEAR */
return 0;
case 0x98: /* INTC_PENDING_IRQ */
return bank->irqs & ~bank->mask & ~bank->fiq;
case 0x9c: /* INTC_PENDING_FIQ */
return bank->irqs & ~bank->mask & bank->fiq;
/* Per-line registers */
case 0x100 ... 0x300: /* INTC_ILR */
bank_no = (offset - 0x100) >> 7;
if (bank_no > s->nbanks)
break;
bank = &s->bank[bank_no];
line_no = (offset & 0x7f) >> 2;
return (bank->priority[line_no] << 2) |
((bank->fiq >> line_no) & 1);
}
OMAP_BAD_REG(addr);
return 0;
}
static void omap2_inth_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
int offset = addr;
int bank_no, line_no;
struct omap_intr_handler_bank_s *bank = NULL;
if ((offset & 0xf80) == 0x80) {
bank_no = (offset & 0x60) >> 5;
if (bank_no < s->nbanks) {
offset &= ~0x60;
bank = &s->bank[bank_no];
} else {
OMAP_BAD_REG(addr);
return;
}
}
switch (offset) {
case 0x10: /* INTC_SYSCONFIG */
s->autoidle &= 4;
s->autoidle |= (value & 1) << 2;
if (value & 2) /* SOFTRESET */
omap_inth_reset(&s->busdev.qdev);
return;
case 0x48: /* INTC_CONTROL */
s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */
if (value & 2) { /* NEWFIQAGR */
qemu_set_irq(s->parent_intr[1], 0);
s->new_agr[1] = ~0;
omap_inth_update(s, 1);
}
if (value & 1) { /* NEWIRQAGR */
qemu_set_irq(s->parent_intr[0], 0);
s->new_agr[0] = ~0;
omap_inth_update(s, 0);
}
return;
case 0x4c: /* INTC_PROTECTION */
/* TODO: Make a bitmap (or sizeof(char)map) of access privileges
* for every register, see Chapter 3 and 4 for privileged mode. */
if (value & 1)
fprintf(stderr, "%s: protection mode enable attempt\n",
__FUNCTION__);
return;
case 0x50: /* INTC_IDLE */
s->autoidle &= ~3;
s->autoidle |= value & 3;
return;
/* Per-bank registers */
case 0x84: /* INTC_MIR */
bank->mask = value;
omap_inth_update(s, 0);
omap_inth_update(s, 1);
return;
case 0x88: /* INTC_MIR_CLEAR */
bank->mask &= ~value;
omap_inth_update(s, 0);
omap_inth_update(s, 1);
return;
case 0x8c: /* INTC_MIR_SET */
bank->mask |= value;
return;
case 0x90: /* INTC_ISR_SET */
bank->irqs |= bank->swi |= value;
omap_inth_update(s, 0);
omap_inth_update(s, 1);
return;
case 0x94: /* INTC_ISR_CLEAR */
bank->swi &= ~value;
bank->irqs = bank->swi & bank->inputs;
return;
/* Per-line registers */
case 0x100 ... 0x300: /* INTC_ILR */
bank_no = (offset - 0x100) >> 7;
if (bank_no > s->nbanks)
break;
bank = &s->bank[bank_no];
line_no = (offset & 0x7f) >> 2;
bank->priority[line_no] = (value >> 2) & 0x3f;
bank->fiq &= ~(1 << line_no);
bank->fiq |= (value & 1) << line_no;
return;
case 0x00: /* INTC_REVISION */
case 0x14: /* INTC_SYSSTATUS */
case 0x40: /* INTC_SIR_IRQ */
case 0x44: /* INTC_SIR_FIQ */
case 0x80: /* INTC_ITR */
case 0x98: /* INTC_PENDING_IRQ */
case 0x9c: /* INTC_PENDING_FIQ */
OMAP_RO_REG(addr);
return;
}
OMAP_BAD_REG(addr);
}
static const MemoryRegionOps omap2_inth_mem_ops = {
.read = omap2_inth_read,
.write = omap2_inth_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static int omap2_intc_init(SysBusDevice *dev)
{
struct omap_intr_handler_s *s;
s = FROM_SYSBUS(struct omap_intr_handler_s, dev);
if (!s->iclk) {
hw_error("omap2-intc: iclk not connected\n");
}
if (!s->fclk) {
hw_error("omap2-intc: fclk not connected\n");
}
s->level_only = 1;
s->nbanks = 3;
sysbus_init_irq(dev, &s->parent_intr[0]);
sysbus_init_irq(dev, &s->parent_intr[1]);
qdev_init_gpio_in(&dev->qdev, omap_set_intr_noedge, s->nbanks * 32);
memory_region_init_io(&s->mmio, &omap2_inth_mem_ops, s,
"omap2-intc", 0x1000);
sysbus_init_mmio(dev, &s->mmio);
return 0;
}
static Property omap2_intc_properties[] = {
DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s,
revision, 0x21),
DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk),
DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk),
DEFINE_PROP_END_OF_LIST(),
};
static void omap2_intc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = omap2_intc_init;
dc->reset = omap_inth_reset;
dc->props = omap2_intc_properties;
}
static const TypeInfo omap2_intc_info = {
.name = "omap2-intc",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct omap_intr_handler_s),
.class_init = omap2_intc_class_init,
};
static void omap_intc_register_types(void)
{
type_register_static(&omap_intc_info);
type_register_static(&omap2_intc_info);
}
type_init(omap_intc_register_types)

1661
hw/intc/openpic.c Normal file

File diff suppressed because it is too large Load diff

74
hw/intc/realview_gic.c Normal file
View file

@ -0,0 +1,74 @@
/*
* ARM RealView Emulation Baseboard Interrupt Controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*/
#include "hw/sysbus.h"
typedef struct {
SysBusDevice busdev;
DeviceState *gic;
MemoryRegion container;
} RealViewGICState;
static void realview_gic_set_irq(void *opaque, int irq, int level)
{
RealViewGICState *s = (RealViewGICState *)opaque;
qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
}
static int realview_gic_init(SysBusDevice *dev)
{
RealViewGICState *s = FROM_SYSBUS(RealViewGICState, dev);
SysBusDevice *busdev;
/* The GICs on the RealView boards have a fixed nonconfigurable
* number of interrupt lines, so we don't need to expose this as
* a qdev property.
*/
int numirq = 96;
s->gic = qdev_create(NULL, "arm_gic");
qdev_prop_set_uint32(s->gic, "num-cpu", 1);
qdev_prop_set_uint32(s->gic, "num-irq", numirq);
qdev_init_nofail(s->gic);
busdev = SYS_BUS_DEVICE(s->gic);
/* Pass through outbound IRQ lines from the GIC */
sysbus_pass_irq(dev, busdev);
/* Pass through inbound GPIO lines to the GIC */
qdev_init_gpio_in(&s->busdev.qdev, realview_gic_set_irq, numirq - 32);
memory_region_init(&s->container, "realview-gic-container", 0x2000);
memory_region_add_subregion(&s->container, 0,
sysbus_mmio_get_region(busdev, 1));
memory_region_add_subregion(&s->container, 0x1000,
sysbus_mmio_get_region(busdev, 0));
sysbus_init_mmio(dev, &s->container);
return 0;
}
static void realview_gic_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
sdc->init = realview_gic_init;
}
static const TypeInfo realview_gic_info = {
.name = "realview_gic",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(RealViewGICState),
.class_init = realview_gic_class_init,
};
static void realview_gic_register_types(void)
{
type_register_static(&realview_gic_info);
}
type_init(realview_gic_register_types)

156
hw/intc/sbi.c Normal file
View file

@ -0,0 +1,156 @@
/*
* QEMU Sparc SBI interrupt controller emulation
*
* Based on slavio_intctl, copyright (c) 2003-2005 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/sysbus.h"
//#define DEBUG_IRQ
#ifdef DEBUG_IRQ
#define DPRINTF(fmt, ...) \
do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...)
#endif
#define MAX_CPUS 16
#define SBI_NREGS 16
typedef struct SBIState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t regs[SBI_NREGS];
uint32_t intreg_pending[MAX_CPUS];
qemu_irq cpu_irqs[MAX_CPUS];
uint32_t pil_out[MAX_CPUS];
} SBIState;
#define SBI_SIZE (SBI_NREGS * 4)
static void sbi_set_irq(void *opaque, int irq, int level)
{
}
static uint64_t sbi_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
SBIState *s = opaque;
uint32_t saddr, ret;
saddr = addr >> 2;
switch (saddr) {
default:
ret = s->regs[saddr];
break;
}
DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
return ret;
}
static void sbi_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned dize)
{
SBIState *s = opaque;
uint32_t saddr;
saddr = addr >> 2;
DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, (int)val);
switch (saddr) {
default:
s->regs[saddr] = val;
break;
}
}
static const MemoryRegionOps sbi_mem_ops = {
.read = sbi_mem_read,
.write = sbi_mem_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static const VMStateDescription vmstate_sbi = {
.name ="sbi",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_UINT32_ARRAY(intreg_pending, SBIState, MAX_CPUS),
VMSTATE_END_OF_LIST()
}
};
static void sbi_reset(DeviceState *d)
{
SBIState *s = container_of(d, SBIState, busdev.qdev);
unsigned int i;
for (i = 0; i < MAX_CPUS; i++) {
s->intreg_pending[i] = 0;
}
}
static int sbi_init1(SysBusDevice *dev)
{
SBIState *s = FROM_SYSBUS(SBIState, dev);
unsigned int i;
qdev_init_gpio_in(&dev->qdev, sbi_set_irq, 32 + MAX_CPUS);
for (i = 0; i < MAX_CPUS; i++) {
sysbus_init_irq(dev, &s->cpu_irqs[i]);
}
memory_region_init_io(&s->iomem, &sbi_mem_ops, s, "sbi", SBI_SIZE);
sysbus_init_mmio(dev, &s->iomem);
return 0;
}
static void sbi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = sbi_init1;
dc->reset = sbi_reset;
dc->vmsd = &vmstate_sbi;
}
static const TypeInfo sbi_info = {
.name = "sbi",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SBIState),
.class_init = sbi_class_init,
};
static void sbi_register_types(void)
{
type_register_static(&sbi_info);
}
type_init(sbi_register_types)

513
hw/intc/sh_intc.c Normal file
View file

@ -0,0 +1,513 @@
/*
* SuperH interrupt controller module
*
* Copyright (c) 2007 Magnus Damm
* Based on sh_timer.c and arm_timer.c by Paul Brook
* Copyright (c) 2005-2006 CodeSourcery.
*
* This code is licensed under the GPL.
*/
#include "hw/sh4/sh_intc.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
//#define DEBUG_INTC
//#define DEBUG_INTC_SOURCES
#define INTC_A7(x) ((x) & 0x1fffffff)
void sh_intc_toggle_source(struct intc_source *source,
int enable_adj, int assert_adj)
{
int enable_changed = 0;
int pending_changed = 0;
int old_pending;
if ((source->enable_count == source->enable_max) && (enable_adj == -1))
enable_changed = -1;
source->enable_count += enable_adj;
if (source->enable_count == source->enable_max)
enable_changed = 1;
source->asserted += assert_adj;
old_pending = source->pending;
source->pending = source->asserted &&
(source->enable_count == source->enable_max);
if (old_pending != source->pending)
pending_changed = 1;
if (pending_changed) {
CPUState *cpu = CPU(sh_env_get_cpu(first_cpu));
if (source->pending) {
source->parent->pending++;
if (source->parent->pending == 1) {
cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
}
} else {
source->parent->pending--;
if (source->parent->pending == 0) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
}
}
}
if (enable_changed || assert_adj || pending_changed) {
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
source->parent->pending,
source->asserted,
source->enable_count,
source->enable_max,
source->vect,
source->asserted ? "asserted " :
assert_adj ? "deasserted" : "",
enable_changed == 1 ? "enabled " :
enable_changed == -1 ? "disabled " : "",
source->pending ? "pending" : "");
#endif
}
}
static void sh_intc_set_irq (void *opaque, int n, int level)
{
struct intc_desc *desc = opaque;
struct intc_source *source = &(desc->sources[n]);
if (level && !source->asserted)
sh_intc_toggle_source(source, 0, 1);
else if (!level && source->asserted)
sh_intc_toggle_source(source, 0, -1);
}
int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
{
unsigned int i;
/* slow: use a linked lists of pending sources instead */
/* wrong: take interrupt priority into account (one list per priority) */
if (imask == 0x0f) {
return -1; /* FIXME, update code to include priority per source */
}
for (i = 0; i < desc->nr_sources; i++) {
struct intc_source *source = desc->sources + i;
if (source->pending) {
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: (%d) returning interrupt source 0x%x\n",
desc->pending, source->vect);
#endif
return source->vect;
}
}
abort();
}
#define INTC_MODE_NONE 0
#define INTC_MODE_DUAL_SET 1
#define INTC_MODE_DUAL_CLR 2
#define INTC_MODE_ENABLE_REG 3
#define INTC_MODE_MASK_REG 4
#define INTC_MODE_IS_PRIO 8
static unsigned int sh_intc_mode(unsigned long address,
unsigned long set_reg, unsigned long clr_reg)
{
if ((address != INTC_A7(set_reg)) &&
(address != INTC_A7(clr_reg)))
return INTC_MODE_NONE;
if (set_reg && clr_reg) {
if (address == INTC_A7(set_reg))
return INTC_MODE_DUAL_SET;
else
return INTC_MODE_DUAL_CLR;
}
if (set_reg)
return INTC_MODE_ENABLE_REG;
else
return INTC_MODE_MASK_REG;
}
static void sh_intc_locate(struct intc_desc *desc,
unsigned long address,
unsigned long **datap,
intc_enum **enums,
unsigned int *first,
unsigned int *width,
unsigned int *modep)
{
unsigned int i, mode;
/* this is slow but works for now */
if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i;
mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
if (mode == INTC_MODE_NONE)
continue;
*modep = mode;
*datap = &mr->value;
*enums = mr->enum_ids;
*first = mr->reg_width - 1;
*width = 1;
return;
}
}
if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i;
mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
if (mode == INTC_MODE_NONE)
continue;
*modep = mode | INTC_MODE_IS_PRIO;
*datap = &pr->value;
*enums = pr->enum_ids;
*first = (pr->reg_width / pr->field_width) - 1;
*width = pr->field_width;
return;
}
}
abort();
}
static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
int enable, int is_group)
{
struct intc_source *source = desc->sources + id;
if (!id)
return;
if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: reserved interrupt source %d modified\n", id);
#endif
return;
}
if (source->vect)
sh_intc_toggle_source(source, enable ? 1 : -1, 0);
#ifdef DEBUG_INTC
else {
printf("setting interrupt group %d to %d\n", id, !!enable);
}
#endif
if ((is_group || !source->vect) && source->next_enum_id) {
sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
}
#ifdef DEBUG_INTC
if (!source->vect) {
printf("setting interrupt group %d to %d - done\n", id, !!enable);
}
#endif
}
static uint64_t sh_intc_read(void *opaque, hwaddr offset,
unsigned size)
{
struct intc_desc *desc = opaque;
intc_enum *enum_ids = NULL;
unsigned int first = 0;
unsigned int width = 0;
unsigned int mode = 0;
unsigned long *valuep;
#ifdef DEBUG_INTC
printf("sh_intc_read 0x%lx\n", (unsigned long) offset);
#endif
sh_intc_locate(desc, (unsigned long)offset, &valuep,
&enum_ids, &first, &width, &mode);
return *valuep;
}
static void sh_intc_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
struct intc_desc *desc = opaque;
intc_enum *enum_ids = NULL;
unsigned int first = 0;
unsigned int width = 0;
unsigned int mode = 0;
unsigned int k;
unsigned long *valuep;
unsigned long mask;
#ifdef DEBUG_INTC
printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
#endif
sh_intc_locate(desc, (unsigned long)offset, &valuep,
&enum_ids, &first, &width, &mode);
switch (mode) {
case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break;
case INTC_MODE_DUAL_SET: value |= *valuep; break;
case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break;
default: abort();
}
for (k = 0; k <= first; k++) {
mask = ((1 << width) - 1) << ((first - k) * width);
if ((*valuep & mask) == (value & mask))
continue;
#if 0
printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
k, first, enum_ids[k], (unsigned int)mask);
#endif
sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
}
*valuep = value;
#ifdef DEBUG_INTC
printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
#endif
}
static const MemoryRegionOps sh_intc_ops = {
.read = sh_intc_read,
.write = sh_intc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id)
{
if (id)
return desc->sources + id;
return NULL;
}
static unsigned int sh_intc_register(MemoryRegion *sysmem,
struct intc_desc *desc,
const unsigned long address,
const char *type,
const char *action,
const unsigned int index)
{
char name[60];
MemoryRegion *iomem, *iomem_p4, *iomem_a7;
if (!address) {
return 0;
}
iomem = &desc->iomem;
iomem_p4 = desc->iomem_aliases + index;
iomem_a7 = iomem_p4 + 1;
#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s"
snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4");
memory_region_init_alias(iomem_p4, name, iomem, INTC_A7(address), 4);
memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4);
snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7");
memory_region_init_alias(iomem_a7, name, iomem, INTC_A7(address), 4);
memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7);
#undef SH_INTC_IOMEM_FORMAT
/* used to increment aliases index */
return 2;
}
static void sh_intc_register_source(struct intc_desc *desc,
intc_enum source,
struct intc_group *groups,
int nr_groups)
{
unsigned int i, k;
struct intc_source *s;
if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i;
for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
if (mr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, mr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i;
for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
if (pr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, pr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = groups + i;
for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
if (gr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, gr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
}
void sh_intc_register_sources(struct intc_desc *desc,
struct intc_vect *vectors,
int nr_vectors,
struct intc_group *groups,
int nr_groups)
{
unsigned int i, k;
struct intc_source *s;
for (i = 0; i < nr_vectors; i++) {
struct intc_vect *vect = vectors + i;
sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
s = sh_intc_source(desc, vect->enum_id);
if (s) {
s->vect = vect->vect;
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
vect->enum_id, s->vect, s->enable_count, s->enable_max);
#endif
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = groups + i;
s = sh_intc_source(desc, gr->enum_id);
s->next_enum_id = gr->enum_ids[0];
for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
if (!gr->enum_ids[k])
continue;
s = sh_intc_source(desc, gr->enum_ids[k - 1]);
s->next_enum_id = gr->enum_ids[k];
}
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered group %d (%d/%d)\n",
gr->enum_id, s->enable_count, s->enable_max);
#endif
}
}
}
int sh_intc_init(MemoryRegion *sysmem,
struct intc_desc *desc,
int nr_sources,
struct intc_mask_reg *mask_regs,
int nr_mask_regs,
struct intc_prio_reg *prio_regs,
int nr_prio_regs)
{
unsigned int i, j;
desc->pending = 0;
desc->nr_sources = nr_sources;
desc->mask_regs = mask_regs;
desc->nr_mask_regs = nr_mask_regs;
desc->prio_regs = prio_regs;
desc->nr_prio_regs = nr_prio_regs;
/* Allocate 4 MemoryRegions per register (2 actions * 2 aliases).
**/
desc->iomem_aliases = g_new0(MemoryRegion,
(nr_mask_regs + nr_prio_regs) * 4);
j = 0;
i = sizeof(struct intc_source) * nr_sources;
desc->sources = g_malloc0(i);
for (i = 0; i < desc->nr_sources; i++) {
struct intc_source *source = desc->sources + i;
source->parent = desc;
}
desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources);
memory_region_init_io(&desc->iomem, &sh_intc_ops, desc,
"interrupt-controller", 0x100000000ULL);
#define INT_REG_PARAMS(reg_struct, type, action, j) \
reg_struct->action##_reg, #type, #action, j
if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i;
j += sh_intc_register(sysmem, desc,
INT_REG_PARAMS(mr, mask, set, j));
j += sh_intc_register(sysmem, desc,
INT_REG_PARAMS(mr, mask, clr, j));
}
}
if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i;
j += sh_intc_register(sysmem, desc,
INT_REG_PARAMS(pr, prio, set, j));
j += sh_intc_register(sysmem, desc,
INT_REG_PARAMS(pr, prio, clr, j));
}
}
#undef INT_REG_PARAMS
return 0;
}
/* Assert level <n> IRL interrupt.
0:deassert. 1:lowest priority,... 15:highest priority. */
void sh_intc_set_irl(void *opaque, int n, int level)
{
struct intc_source *s = opaque;
int i, irl = level ^ 15;
for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) {
if (i == irl)
sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1);
else
if (s->asserted)
sh_intc_toggle_source(s, 0, -1);
}
}

471
hw/intc/slavio_intctl.c Normal file
View file

@ -0,0 +1,471 @@
/*
* QEMU Sparc SLAVIO interrupt controller emulation
*
* Copyright (c) 2003-2005 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/sparc/sun4m.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
#include "trace.h"
//#define DEBUG_IRQ_COUNT
/*
* Registers of interrupt controller in sun4m.
*
* This is the interrupt controller part of chip STP2001 (Slave I/O), also
* produced as NCR89C105. See
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
*
* There is a system master controller and one for each cpu.
*
*/
#define MAX_CPUS 16
#define MAX_PILS 16
struct SLAVIO_INTCTLState;
typedef struct SLAVIO_CPUINTCTLState {
MemoryRegion iomem;
struct SLAVIO_INTCTLState *master;
uint32_t intreg_pending;
uint32_t cpu;
uint32_t irl_out;
} SLAVIO_CPUINTCTLState;
typedef struct SLAVIO_INTCTLState {
SysBusDevice busdev;
MemoryRegion iomem;
#ifdef DEBUG_IRQ_COUNT
uint64_t irq_count[32];
#endif
qemu_irq cpu_irqs[MAX_CPUS][MAX_PILS];
SLAVIO_CPUINTCTLState slaves[MAX_CPUS];
uint32_t intregm_pending;
uint32_t intregm_disabled;
uint32_t target_cpu;
} SLAVIO_INTCTLState;
#define INTCTL_MAXADDR 0xf
#define INTCTL_SIZE (INTCTL_MAXADDR + 1)
#define INTCTLM_SIZE 0x14
#define MASTER_IRQ_MASK ~0x0fa2007f
#define MASTER_DISABLE 0x80000000
#define CPU_SOFTIRQ_MASK 0xfffe0000
#define CPU_IRQ_INT15_IN (1 << 15)
#define CPU_IRQ_TIMER_IN (1 << 14)
static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs);
// per-cpu interrupt controller
static uint64_t slavio_intctl_mem_readl(void *opaque, hwaddr addr,
unsigned size)
{
SLAVIO_CPUINTCTLState *s = opaque;
uint32_t saddr, ret;
saddr = addr >> 2;
switch (saddr) {
case 0:
ret = s->intreg_pending;
break;
default:
ret = 0;
break;
}
trace_slavio_intctl_mem_readl(s->cpu, addr, ret);
return ret;
}
static void slavio_intctl_mem_writel(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
SLAVIO_CPUINTCTLState *s = opaque;
uint32_t saddr;
saddr = addr >> 2;
trace_slavio_intctl_mem_writel(s->cpu, addr, val);
switch (saddr) {
case 1: // clear pending softints
val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN;
s->intreg_pending &= ~val;
slavio_check_interrupts(s->master, 1);
trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending);
break;
case 2: // set softint
val &= CPU_SOFTIRQ_MASK;
s->intreg_pending |= val;
slavio_check_interrupts(s->master, 1);
trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending);
break;
default:
break;
}
}
static const MemoryRegionOps slavio_intctl_mem_ops = {
.read = slavio_intctl_mem_readl,
.write = slavio_intctl_mem_writel,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
// master system interrupt controller
static uint64_t slavio_intctlm_mem_readl(void *opaque, hwaddr addr,
unsigned size)
{
SLAVIO_INTCTLState *s = opaque;
uint32_t saddr, ret;
saddr = addr >> 2;
switch (saddr) {
case 0:
ret = s->intregm_pending & ~MASTER_DISABLE;
break;
case 1:
ret = s->intregm_disabled & MASTER_IRQ_MASK;
break;
case 4:
ret = s->target_cpu;
break;
default:
ret = 0;
break;
}
trace_slavio_intctlm_mem_readl(addr, ret);
return ret;
}
static void slavio_intctlm_mem_writel(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
SLAVIO_INTCTLState *s = opaque;
uint32_t saddr;
saddr = addr >> 2;
trace_slavio_intctlm_mem_writel(addr, val);
switch (saddr) {
case 2: // clear (enable)
// Force clear unused bits
val &= MASTER_IRQ_MASK;
s->intregm_disabled &= ~val;
trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled);
slavio_check_interrupts(s, 1);
break;
case 3: // set (disable; doesn't affect pending)
// Force clear unused bits
val &= MASTER_IRQ_MASK;
s->intregm_disabled |= val;
slavio_check_interrupts(s, 1);
trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled);
break;
case 4:
s->target_cpu = val & (MAX_CPUS - 1);
slavio_check_interrupts(s, 1);
trace_slavio_intctlm_mem_writel_target(s->target_cpu);
break;
default:
break;
}
}
static const MemoryRegionOps slavio_intctlm_mem_ops = {
.read = slavio_intctlm_mem_readl,
.write = slavio_intctlm_mem_writel,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
void slavio_pic_info(Monitor *mon, DeviceState *dev)
{
SysBusDevice *sd;
SLAVIO_INTCTLState *s;
int i;
sd = SYS_BUS_DEVICE(dev);
s = FROM_SYSBUS(SLAVIO_INTCTLState, sd);
for (i = 0; i < MAX_CPUS; i++) {
monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i,
s->slaves[i].intreg_pending);
}
monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n",
s->intregm_pending, s->intregm_disabled);
}
void slavio_irq_info(Monitor *mon, DeviceState *dev)
{
#ifndef DEBUG_IRQ_COUNT
monitor_printf(mon, "irq statistic code not compiled.\n");
#else
SysBusDevice *sd;
SLAVIO_INTCTLState *s;
int i;
int64_t count;
sd = SYS_BUS_DEVICE(dev);
s = FROM_SYSBUS(SLAVIO_INTCTLState, sd);
monitor_printf(mon, "IRQ statistics:\n");
for (i = 0; i < 32; i++) {
count = s->irq_count[i];
if (count > 0)
monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
}
#endif
}
static const uint32_t intbit_to_level[] = {
2, 3, 5, 7, 9, 11, 13, 2, 3, 5, 7, 9, 11, 13, 12, 12,
6, 13, 4, 10, 8, 9, 11, 0, 0, 0, 0, 15, 15, 15, 15, 0,
};
static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs)
{
uint32_t pending = s->intregm_pending, pil_pending;
unsigned int i, j;
pending &= ~s->intregm_disabled;
trace_slavio_check_interrupts(pending, s->intregm_disabled);
for (i = 0; i < MAX_CPUS; i++) {
pil_pending = 0;
/* If we are the current interrupt target, get hard interrupts */
if (pending && !(s->intregm_disabled & MASTER_DISABLE) &&
(i == s->target_cpu)) {
for (j = 0; j < 32; j++) {
if ((pending & (1 << j)) && intbit_to_level[j]) {
pil_pending |= 1 << intbit_to_level[j];
}
}
}
/* Calculate current pending hard interrupts for display */
s->slaves[i].intreg_pending &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN |
CPU_IRQ_TIMER_IN;
if (i == s->target_cpu) {
for (j = 0; j < 32; j++) {
if ((s->intregm_pending & (1 << j)) && intbit_to_level[j]) {
s->slaves[i].intreg_pending |= 1 << intbit_to_level[j];
}
}
}
/* Level 15 and CPU timer interrupts are only masked when
the MASTER_DISABLE bit is set */
if (!(s->intregm_disabled & MASTER_DISABLE)) {
pil_pending |= s->slaves[i].intreg_pending &
(CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN);
}
/* Add soft interrupts */
pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16;
if (set_irqs) {
/* Since there is not really an interrupt 0 (and pil_pending
* and irl_out bit zero are thus always zero) there is no need
* to do anything with cpu_irqs[i][0] and it is OK not to do
* the j=0 iteration of this loop.
*/
for (j = MAX_PILS-1; j > 0; j--) {
if (pil_pending & (1 << j)) {
if (!(s->slaves[i].irl_out & (1 << j))) {
qemu_irq_raise(s->cpu_irqs[i][j]);
}
} else {
if (s->slaves[i].irl_out & (1 << j)) {
qemu_irq_lower(s->cpu_irqs[i][j]);
}
}
}
}
s->slaves[i].irl_out = pil_pending;
}
}
/*
* "irq" here is the bit number in the system interrupt register to
* separate serial and keyboard interrupts sharing a level.
*/
static void slavio_set_irq(void *opaque, int irq, int level)
{
SLAVIO_INTCTLState *s = opaque;
uint32_t mask = 1 << irq;
uint32_t pil = intbit_to_level[irq];
unsigned int i;
trace_slavio_set_irq(s->target_cpu, irq, pil, level);
if (pil > 0) {
if (level) {
#ifdef DEBUG_IRQ_COUNT
s->irq_count[pil]++;
#endif
s->intregm_pending |= mask;
if (pil == 15) {
for (i = 0; i < MAX_CPUS; i++) {
s->slaves[i].intreg_pending |= 1 << pil;
}
}
} else {
s->intregm_pending &= ~mask;
if (pil == 15) {
for (i = 0; i < MAX_CPUS; i++) {
s->slaves[i].intreg_pending &= ~(1 << pil);
}
}
}
slavio_check_interrupts(s, 1);
}
}
static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level)
{
SLAVIO_INTCTLState *s = opaque;
trace_slavio_set_timer_irq_cpu(cpu, level);
if (level) {
s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN;
} else {
s->slaves[cpu].intreg_pending &= ~CPU_IRQ_TIMER_IN;
}
slavio_check_interrupts(s, 1);
}
static void slavio_set_irq_all(void *opaque, int irq, int level)
{
if (irq < 32) {
slavio_set_irq(opaque, irq, level);
} else {
slavio_set_timer_irq_cpu(opaque, irq - 32, level);
}
}
static int vmstate_intctl_post_load(void *opaque, int version_id)
{
SLAVIO_INTCTLState *s = opaque;
slavio_check_interrupts(s, 0);
return 0;
}
static const VMStateDescription vmstate_intctl_cpu = {
.name ="slavio_intctl_cpu",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_intctl = {
.name ="slavio_intctl",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = vmstate_intctl_post_load,
.fields = (VMStateField []) {
VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1,
vmstate_intctl_cpu, SLAVIO_CPUINTCTLState),
VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState),
VMSTATE_UINT32(intregm_disabled, SLAVIO_INTCTLState),
VMSTATE_UINT32(target_cpu, SLAVIO_INTCTLState),
VMSTATE_END_OF_LIST()
}
};
static void slavio_intctl_reset(DeviceState *d)
{
SLAVIO_INTCTLState *s = container_of(d, SLAVIO_INTCTLState, busdev.qdev);
int i;
for (i = 0; i < MAX_CPUS; i++) {
s->slaves[i].intreg_pending = 0;
s->slaves[i].irl_out = 0;
}
s->intregm_disabled = ~MASTER_IRQ_MASK;
s->intregm_pending = 0;
s->target_cpu = 0;
slavio_check_interrupts(s, 0);
}
static int slavio_intctl_init1(SysBusDevice *dev)
{
SLAVIO_INTCTLState *s = FROM_SYSBUS(SLAVIO_INTCTLState, dev);
unsigned int i, j;
char slave_name[45];
qdev_init_gpio_in(&dev->qdev, slavio_set_irq_all, 32 + MAX_CPUS);
memory_region_init_io(&s->iomem, &slavio_intctlm_mem_ops, s,
"master-interrupt-controller", INTCTLM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
for (i = 0; i < MAX_CPUS; i++) {
snprintf(slave_name, sizeof(slave_name),
"slave-interrupt-controller-%i", i);
for (j = 0; j < MAX_PILS; j++) {
sysbus_init_irq(dev, &s->cpu_irqs[i][j]);
}
memory_region_init_io(&s->slaves[i].iomem, &slavio_intctl_mem_ops,
&s->slaves[i], slave_name, INTCTL_SIZE);
sysbus_init_mmio(dev, &s->slaves[i].iomem);
s->slaves[i].cpu = i;
s->slaves[i].master = s;
}
return 0;
}
static void slavio_intctl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = slavio_intctl_init1;
dc->reset = slavio_intctl_reset;
dc->vmsd = &vmstate_intctl;
}
static const TypeInfo slavio_intctl_info = {
.name = "slavio_intctl",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SLAVIO_INTCTLState),
.class_init = slavio_intctl_class_init,
};
static void slavio_intctl_register_types(void)
{
type_register_static(&slavio_intctl_info);
}
type_init(slavio_intctl_register_types)

208
hw/intc/sun4c_intctl.c Normal file
View file

@ -0,0 +1,208 @@
/*
* QEMU Sparc Sun4c interrupt controller emulation
*
* Based on slavio_intctl, copyright (c) 2003-2005 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/hw.h"
#include "hw/sparc/sun4m.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
//#define DEBUG_IRQ_COUNT
//#define DEBUG_IRQ
#ifdef DEBUG_IRQ
#define DPRINTF(fmt, ...) \
do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...)
#endif
/*
* Registers of interrupt controller in sun4c.
*
*/
#define MAX_PILS 16
typedef struct Sun4c_INTCTLState {
SysBusDevice busdev;
MemoryRegion iomem;
#ifdef DEBUG_IRQ_COUNT
uint64_t irq_count;
#endif
qemu_irq cpu_irqs[MAX_PILS];
const uint32_t *intbit_to_level;
uint32_t pil_out;
uint8_t reg;
uint8_t pending;
} Sun4c_INTCTLState;
#define INTCTL_SIZE 1
static void sun4c_check_interrupts(void *opaque);
static uint64_t sun4c_intctl_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
Sun4c_INTCTLState *s = opaque;
uint32_t ret;
ret = s->reg;
DPRINTF("read reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
return ret;
}
static void sun4c_intctl_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
Sun4c_INTCTLState *s = opaque;
DPRINTF("write reg 0x" TARGET_FMT_plx " = %x\n", addr, (unsigned)val);
val &= 0xbf;
s->reg = val;
sun4c_check_interrupts(s);
}
static const MemoryRegionOps sun4c_intctl_mem_ops = {
.read = sun4c_intctl_mem_read,
.write = sun4c_intctl_mem_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static const uint32_t intbit_to_level[] = { 0, 1, 4, 6, 8, 10, 0, 14, };
static void sun4c_check_interrupts(void *opaque)
{
Sun4c_INTCTLState *s = opaque;
uint32_t pil_pending;
unsigned int i;
pil_pending = 0;
if (s->pending && !(s->reg & 0x80000000)) {
for (i = 0; i < 8; i++) {
if (s->pending & (1 << i))
pil_pending |= 1 << intbit_to_level[i];
}
}
for (i = 0; i < MAX_PILS; i++) {
if (pil_pending & (1 << i)) {
if (!(s->pil_out & (1 << i)))
qemu_irq_raise(s->cpu_irqs[i]);
} else {
if (s->pil_out & (1 << i))
qemu_irq_lower(s->cpu_irqs[i]);
}
}
s->pil_out = pil_pending;
}
/*
* "irq" here is the bit number in the system interrupt register
*/
static void sun4c_set_irq(void *opaque, int irq, int level)
{
Sun4c_INTCTLState *s = opaque;
uint32_t mask = 1 << irq;
uint32_t pil = intbit_to_level[irq];
DPRINTF("Set irq %d -> pil %d level %d\n", irq, pil,
level);
if (pil > 0) {
if (level) {
#ifdef DEBUG_IRQ_COUNT
s->irq_count++;
#endif
s->pending |= mask;
} else {
s->pending &= ~mask;
}
sun4c_check_interrupts(s);
}
}
static const VMStateDescription vmstate_sun4c_intctl = {
.name ="sun4c_intctl",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_UINT8(reg, Sun4c_INTCTLState),
VMSTATE_UINT8(pending, Sun4c_INTCTLState),
VMSTATE_END_OF_LIST()
}
};
static void sun4c_intctl_reset(DeviceState *d)
{
Sun4c_INTCTLState *s = container_of(d, Sun4c_INTCTLState, busdev.qdev);
s->reg = 1;
s->pending = 0;
}
static int sun4c_intctl_init1(SysBusDevice *dev)
{
Sun4c_INTCTLState *s = FROM_SYSBUS(Sun4c_INTCTLState, dev);
unsigned int i;
memory_region_init_io(&s->iomem, &sun4c_intctl_mem_ops, s,
"intctl", INTCTL_SIZE);
sysbus_init_mmio(dev, &s->iomem);
qdev_init_gpio_in(&dev->qdev, sun4c_set_irq, 8);
for (i = 0; i < MAX_PILS; i++) {
sysbus_init_irq(dev, &s->cpu_irqs[i]);
}
return 0;
}
static void sun4c_intctl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = sun4c_intctl_init1;
dc->reset = sun4c_intctl_reset;
dc->vmsd = &vmstate_sun4c_intctl;
}
static const TypeInfo sun4c_intctl_info = {
.name = "sun4c_intctl",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Sun4c_INTCTLState),
.class_init = sun4c_intctl_class_init,
};
static void sun4c_intctl_register_types(void)
{
type_register_static(&sun4c_intctl_info);
}
type_init(sun4c_intctl_register_types)