hw/armv7m_nvic: Use MemoryRegions for NVIC specific registers
Implement the NVIC specific register areas using a set of overlaid MemoryRegions in a container, rather than by having the arm_gic read/write functions use special purpose callbacks. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
b3387ede2f
commit
2a29ddee82
33
hw/arm_gic.c
33
hw/arm_gic.c
@ -37,17 +37,17 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef NVIC
|
||||
static const uint8_t gic_id[] =
|
||||
{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
|
||||
/* The NVIC has 16 internal vectors. However these are not exposed
|
||||
through the normal GIC interface. */
|
||||
#define GIC_BASE_IRQ 32
|
||||
#else
|
||||
static const uint8_t gic_id[] =
|
||||
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
#define GIC_BASE_IRQ 0
|
||||
#endif
|
||||
|
||||
static const uint8_t gic_id[] = {
|
||||
0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
|
||||
};
|
||||
|
||||
#define FROM_SYSBUSGIC(type, dev) \
|
||||
DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev))
|
||||
|
||||
@ -312,7 +312,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
cpu = gic_get_current_cpu(s);
|
||||
cm = 1 << cpu;
|
||||
if (offset < 0x100) {
|
||||
#ifndef NVIC
|
||||
if (offset == 0)
|
||||
return s->enabled;
|
||||
if (offset == 4)
|
||||
@ -323,7 +322,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
/* Interrupt Security , RAZ/WI */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
goto bad_reg;
|
||||
} else if (offset < 0x200) {
|
||||
/* Interrupt Set/Clear Enable. */
|
||||
@ -385,6 +383,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
} else {
|
||||
res = GIC_TARGET(irq);
|
||||
}
|
||||
#endif
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
|
||||
@ -397,7 +396,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
if (GIC_TEST_TRIGGER(irq + i))
|
||||
res |= (2 << (i * 2));
|
||||
}
|
||||
#endif
|
||||
} else if (offset < 0xfe0) {
|
||||
goto bad_reg;
|
||||
} else /* offset >= 0xfe0 */ {
|
||||
@ -424,13 +422,6 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
|
||||
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t val;
|
||||
#ifdef NVIC
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
uint32_t addr;
|
||||
addr = offset;
|
||||
if (addr < 0x100 || addr > 0xd00)
|
||||
return nvic_readl(s, addr);
|
||||
#endif
|
||||
val = gic_dist_readw(opaque, offset);
|
||||
val |= gic_dist_readw(opaque, offset + 2) << 16;
|
||||
return val;
|
||||
@ -446,9 +437,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
|
||||
cpu = gic_get_current_cpu(s);
|
||||
if (offset < 0x100) {
|
||||
#ifdef NVIC
|
||||
goto bad_reg;
|
||||
#else
|
||||
if (offset == 0) {
|
||||
s->enabled = (value & 1);
|
||||
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
|
||||
@ -459,7 +447,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
} else {
|
||||
goto bad_reg;
|
||||
}
|
||||
#endif
|
||||
} else if (offset < 0x180) {
|
||||
/* Interrupt Set Enable. */
|
||||
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
|
||||
@ -552,6 +539,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
else if (irq < GIC_INTERNAL)
|
||||
value = ALL_CPU_MASK;
|
||||
s->irq_target[irq] = value & ALL_CPU_MASK;
|
||||
#endif
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
|
||||
@ -571,7 +559,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
GIC_CLEAR_TRIGGER(irq + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* 0xf00 is only handled for 32-bit writes. */
|
||||
goto bad_reg;
|
||||
@ -593,14 +580,6 @@ static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
#ifdef NVIC
|
||||
uint32_t addr;
|
||||
addr = offset;
|
||||
if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
|
||||
nvic_writel(s, addr, value);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (offset == 0xf00) {
|
||||
int cpu;
|
||||
int irq;
|
||||
|
@ -30,9 +30,16 @@ typedef struct {
|
||||
int64_t tick;
|
||||
QEMUTimer *timer;
|
||||
} systick;
|
||||
MemoryRegion sysregmem;
|
||||
MemoryRegion gic_iomem_alias;
|
||||
MemoryRegion container;
|
||||
uint32_t num_irq;
|
||||
} nvic_state;
|
||||
|
||||
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
|
||||
|
||||
@ -358,12 +365,54 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
|
||||
case 0xd38: /* Bus Fault Address. */
|
||||
case 0xd3c: /* Aux Fault Status. */
|
||||
goto bad_reg;
|
||||
case 0xf00: /* Software Triggered Interrupt Register */
|
||||
if ((value & 0x1ff) < s->num_irq) {
|
||||
gic_set_pending_private(&s->gic, 0, value & 0x1ff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bad_reg:
|
||||
hw_error("NVIC: Bad write offset 0x%x\n", offset);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t nvic_sysreg_read(void *opaque, target_phys_addr_t addr,
|
||||
unsigned size)
|
||||
{
|
||||
/* At the moment we only support the ID registers for byte/word access.
|
||||
* This is not strictly correct as a few of the other registers also
|
||||
* allow byte access.
|
||||
*/
|
||||
uint32_t offset = addr;
|
||||
if (offset >= 0xfe0) {
|
||||
if (offset & 3) {
|
||||
return 0;
|
||||
}
|
||||
return nvic_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
if (size == 4) {
|
||||
return nvic_readl(opaque, offset);
|
||||
}
|
||||
hw_error("NVIC: Bad read of size %d at offset 0x%x\n", size, offset);
|
||||
}
|
||||
|
||||
static void nvic_sysreg_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
uint32_t offset = addr;
|
||||
if (size == 4) {
|
||||
nvic_writel(opaque, offset, value);
|
||||
return;
|
||||
}
|
||||
hw_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,
|
||||
@ -399,7 +448,30 @@ static int armv7m_nvic_init(SysBusDevice *dev)
|
||||
/* The NVIC always has only one CPU */
|
||||
s->gic.num_cpu = 1;
|
||||
gic_init(&s->gic, s->num_irq);
|
||||
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem);
|
||||
/* 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, 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);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user