From b85f62d7811c80646065f8f96c2248ea86cfd911 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Fri, 13 Apr 2012 11:39:06 +0000 Subject: [PATCH 01/16] Fix bit test in Exynos4210 UART emulation to use & instead of && * hw/exynos4210_uart.c: s/&&/&/ Signed-off-by: Daniel P. Berrange Signed-off-by: Peter Maydell --- hw/exynos4210_uart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c index 73a9c18f30..ccc47804f9 100644 --- a/hw/exynos4210_uart.c +++ b/hw/exynos4210_uart.c @@ -246,7 +246,7 @@ static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) uint32_t level = 0; uint32_t reg; - reg = (s->reg[I_(UFCON)] && UFCON_Tx_FIFO_TRIGGER_LEVEL) >> + reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; switch (s->channel) { @@ -275,9 +275,9 @@ static void exynos4210_uart_update_irq(Exynos4210UartState *s) * The Tx interrupt is always requested if the number of data in the * transmit FIFO is smaller than the trigger level. */ - if (s->reg[I_(UFCON)] && UFCON_FIFO_ENABLE) { + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - uint32_t count = (s->reg[I_(UFSTAT)] && UFSTAT_Tx_FIFO_COUNT) >> + uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> UFSTAT_Tx_FIFO_COUNT_SHIFT; if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { From 3f088e36de5a72a69e530d4bbf1fabaa507da0db Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Fri, 13 Apr 2012 11:39:06 +0000 Subject: [PATCH 02/16] ARM: Exynos4210: Drop gic_cpu_write() after initialization. Remove gic_cpu_write() call after initialization that was emulating functionality of earliest SOC bootloader which enables external GIC CPU1 interface. Instead introduce Exynos4210-specific secondary CPU bootloader, which enables both Internal and External GIC CPU1 interfaces. Signed-off-by: Evgeny Voevodin Signed-off-by: Peter Maydell --- hw/exynos4210.c | 30 ++++++++++++++++++++++++++++++ hw/exynos4210.h | 3 +++ hw/exynos4210_gic.c | 2 -- hw/exynos4_boards.c | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hw/exynos4210.c b/hw/exynos4210.c index f904370505..afc4bdc7e0 100644 --- a/hw/exynos4210.c +++ b/hw/exynos4210.c @@ -25,6 +25,7 @@ #include "sysemu.h" #include "sysbus.h" #include "arm-misc.h" +#include "loader.h" #include "exynos4210.h" #define EXYNOS4210_CHIPID_ADDR 0x10000000 @@ -64,6 +65,35 @@ static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43, 0x09, 0x00, 0x00, 0x00 }; +void exynos4210_write_secondary(CPUARMState *env, + const struct arm_boot_info *info) +{ + int n; + uint32_t smpboot[] = { + 0xe59f3024, /* ldr r3, External gic_cpu_if */ + 0xe59f2024, /* ldr r2, Internal gic_cpu_if */ + 0xe59f0024, /* ldr r0, startaddr */ + 0xe3a01001, /* mov r1, #1 */ + 0xe5821000, /* str r1, [r2] */ + 0xe5831000, /* str r1, [r3] */ + 0xe320f003, /* wfi */ + 0xe5901000, /* ldr r1, [r0] */ + 0xe1110001, /* tst r1, r1 */ + 0x0afffffb, /* beq */ + 0xe12fff11, /* bx r1 */ + EXYNOS4210_EXT_GIC_CPU_BASE_ADDR, + 0, /* gic_cpu_if: base address of Internal GIC CPU interface */ + 0 /* bootreg: Boot register address is held here */ + }; + smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr; + smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr; + for (n = 0; n < ARRAY_SIZE(smpboot); n++) { + smpboot[n] = tswap32(smpboot[n]); + } + rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), + info->smp_loader_start); +} + Exynos4210State *exynos4210_init(MemoryRegion *system_mem, unsigned long ram_size) { diff --git a/hw/exynos4210.h b/hw/exynos4210.h index c112e03bfb..f7c7027302 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -97,6 +97,9 @@ typedef struct Exynos4210State { MemoryRegion bootreg_mem; } Exynos4210State; +void exynos4210_write_secondary(CPUARMState *env, + const struct arm_boot_info *info); + Exynos4210State *exynos4210_init(MemoryRegion *system_mem, unsigned long ram_size); diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index ec13140f9f..3ba9063f3c 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -321,8 +321,6 @@ static int exynos4210_gic_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->cpu_container); sysbus_init_mmio(dev, &s->dist_container); - gic_cpu_write(&s->gic, 1, 0, 1); - return 0; } diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c index 553a02b910..ea32c51dcc 100644 --- a/hw/exynos4_boards.c +++ b/hw/exynos4_boards.c @@ -70,6 +70,7 @@ static struct arm_boot_info exynos4_board_binfo = { .loader_start = EXYNOS4210_BASE_BOOT_ADDR, .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, .nb_cpus = EXYNOS4210_NCPUS, + .write_secondary_boot = exynos4210_write_secondary, }; static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS]; From 5181b50fc89d321cf79ed2f2fff5bec0b55e3011 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Fri, 13 Apr 2012 11:39:06 +0000 Subject: [PATCH 03/16] hw/exynos4210_combiner.c: Drop excessive read/write access check. Access to reserved area at offset higher than 0x3c is allowed in External Combiner. Samsung Galaxy Kernel implements this. So, drop excessive checks in read/write functions. Signed-off-by: Evgeny Voevodin Signed-off-by: Peter Maydell --- hw/exynos4210_combiner.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c index 6110c19d5d..80af22cc33 100644 --- a/hw/exynos4210_combiner.c +++ b/hw/exynos4210_combiner.c @@ -184,11 +184,6 @@ exynos4210_combiner_read(void *opaque, target_phys_addr_t offset, unsigned size) uint32_t reg_n; /* Register number inside the quad */ uint32_t val; - if (s->external && (offset > 0x3c && offset != 0x100)) { - hw_error("exynos4210.combiner: unallowed read access at offset 0x" - TARGET_FMT_plx "\n", offset); - } - req_quad_base_n = offset >> 4; grp_quad_base_n = req_quad_base_n << 2; reg_n = (offset - (req_quad_base_n << 4)) >> 2; @@ -281,11 +276,6 @@ static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset, uint32_t grp_quad_base_n; /* Base of group quad */ uint32_t reg_n; /* Register number inside the quad */ - if (s->external && (offset > 0x3c && offset != 0x100)) { - hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); - } - req_quad_base_n = offset >> 4; grp_quad_base_n = req_quad_base_n << 2; reg_n = (offset - (req_quad_base_n << 4)) >> 2; From 386e29554e8f1a7e910682789418aed2094b4ef6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:07 +0000 Subject: [PATCH 04/16] hw/arm_gic: Move NCPU definition to arm_gic.c Move the NCPU definition to arm_gic.c: the maximum number of CPU interfaces is defined by the GIC architecture specification to be 8, so we don't need to have this #define in each of the sources files which currently includes arm_gic.c. Signed-off-by: Peter Maydell Reviewed-by: Evgeny Voevodin --- hw/a15mpcore.c | 8 +------- hw/a9mpcore.c | 8 +------- hw/arm11mpcore.c | 2 -- hw/arm_gic.c | 13 ++++++++++++- hw/armv7m_nvic.c | 1 - hw/exynos4210_gic.c | 9 ++++----- hw/realview_gic.c | 4 +--- 7 files changed, 19 insertions(+), 26 deletions(-) diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 71142e51f5..67206ec870 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -21,10 +21,8 @@ #include "sysbus.h" /* Configuration for arm_gic.c: - * max number of CPUs, how to ID current CPU + * how to ID current CPU */ -#define NCPU 4 - static inline int gic_get_current_cpu(void) { return cpu_single_env->cpu_index; @@ -45,10 +43,6 @@ static int a15mp_priv_init(SysBusDevice *dev) { A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev); - if (s->num_cpu > NCPU) { - hw_error("a15mp_priv_init: num-cpu may not be more than %d\n", NCPU); - } - gic_init(&s->gic, s->num_cpu, s->num_irq); /* Memory map (addresses are offsets from PERIPHBASE): diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 03b128ce5b..5bbe3c76c5 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -11,10 +11,8 @@ #include "sysbus.h" /* Configuration for arm_gic.c: - * max number of CPUs, how to ID current CPU + * how to ID current CPU */ -#define NCPU 4 - static inline int gic_get_current_cpu(void) { @@ -149,10 +147,6 @@ static int a9mp_priv_init(SysBusDevice *dev) SysBusDevice *busdev; int i; - if (s->num_cpu > NCPU) { - hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU); - } - gic_init(&s->gic, s->num_cpu, s->num_irq); s->mptimer = qdev_create(NULL, "arm_mptimer"); diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index ba6a89d3ed..99c18269c1 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -10,8 +10,6 @@ #include "sysbus.h" #include "qemu-timer.h" -#define NCPU 4 - static inline int gic_get_current_cpu(void) { diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 6b34c06a8f..f64a0015f4 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -15,6 +15,13 @@ #define GIC_MAXIRQ 1020 /* First 32 are private to each CPU (SGIs and PPIs). */ #define GIC_INTERNAL 32 +/* Maximum number of possible CPU interfaces, determined by GIC architecture */ +#ifdef NVIC +#define NCPU 1 +#else +#define NCPU 8 +#endif + //#define DEBUG_GIC #ifdef DEBUG_GIC @@ -50,7 +57,7 @@ typedef struct gic_irq_state unsigned trigger:1; /* nonzero = edge triggered. */ } gic_irq_state; -#define ALL_CPU_MASK ((1 << NCPU) - 1) +#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) #if NCPU > 1 #define NUM_CPU(s) ((s)->num_cpu) #else @@ -813,6 +820,10 @@ static void gic_init(gic_state *s, int num_irq) #if NCPU > 1 s->num_cpu = num_cpu; + if (s->num_cpu > NCPU) { + hw_error("requested %u CPUs exceeds GIC maximum %d\n", + num_cpu, NCPU); + } #endif s->num_irq = num_irq + GIC_BASE_IRQ; if (s->num_irq > GIC_MAXIRQ) { diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 3210129c3f..bdab709798 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -15,7 +15,6 @@ #include "arm-misc.h" #include "exec-memory.h" -#define NCPU 1 #define NVIC 1 /* Only a single "CPU" interface is present. */ diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index 3ba9063f3c..426f540174 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -174,7 +174,6 @@ combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { }; #define EXYNOS4210_GIC_NIRQ 160 -#define NCPU EXYNOS4210_NCPUS #define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000 #define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000 @@ -275,8 +274,8 @@ typedef struct { gic_state gic; MemoryRegion cpu_container; MemoryRegion dist_container; - MemoryRegion cpu_alias[NCPU]; - MemoryRegion dist_alias[NCPU]; + MemoryRegion cpu_alias[EXYNOS4210_NCPUS]; + MemoryRegion dist_alias[EXYNOS4210_NCPUS]; uint32_t num_cpu; } Exynos4210GicState; @@ -359,7 +358,7 @@ type_init(exynos4210_gic_register_types) typedef struct { SysBusDevice busdev; - qemu_irq pic_irq[NCPU]; /* output IRQs to PICs */ + qemu_irq pic_irq[EXYNOS4210_NCPUS]; /* output IRQs to PICs */ uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */ } Exynos4210IRQGateState; @@ -424,7 +423,7 @@ static int exynos4210_irq_gate_init(SysBusDevice *dev) EXYNOS4210_IRQ_GATE_NINPUTS); /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < NCPU; i++) { + for (i = 0; i < EXYNOS4210_NCPUS; i++) { sysbus_init_irq(dev, &s->pic_irq[i]); } diff --git a/hw/realview_gic.c b/hw/realview_gic.c index 071ef13c9e..d114242f27 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -9,8 +9,6 @@ #include "sysbus.h" -#define NCPU 1 - /* Only a single "CPU" interface is present. */ static inline int gic_get_current_cpu(void) @@ -40,7 +38,7 @@ static int realview_gic_init(SysBusDevice *dev) * number of interrupt lines, so we don't need to expose this as * a qdev property. */ - gic_init(&s->gic, 96); + gic_init(&s->gic, 1, 96); realview_gic_map_setup(s); sysbus_init_mmio(dev, &s->container); return 0; From 926c4aff6ecf2b26b3508773196314a774bf5c4c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:07 +0000 Subject: [PATCH 05/16] hw/arm_gic: Move gic_get_current_cpu into arm_gic.c Move the gic_get_current_cpu() function into arm_gic.c. There are only two implementations: (1) "get the index of the currently executing CPU", used by all multicore GICs, and (2) "always 0", used by all GICs instantiated with a single CPU interface (the Realview board GIC and the v7M NVIC). So we can move this into the main GIC source file. Signed-off-by: Peter Maydell Reviewed-by: Evgeny Voevodin --- hw/a15mpcore.c | 8 -------- hw/a9mpcore.c | 9 --------- hw/arm11mpcore.c | 6 ------ hw/arm_gic.c | 20 +++++++++++++++----- hw/armv7m_nvic.c | 7 ------- hw/exynos4210_gic.c | 6 ------ hw/realview_gic.c | 7 ------- 7 files changed, 15 insertions(+), 48 deletions(-) diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 67206ec870..2e2ed423da 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -20,14 +20,6 @@ #include "sysbus.h" -/* Configuration for arm_gic.c: - * how to ID current CPU - */ -static inline int gic_get_current_cpu(void) -{ - return cpu_single_env->cpu_index; -} - #include "arm_gic.c" /* A15MP private memory region. */ diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 5bbe3c76c5..1d83c37ad5 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -10,15 +10,6 @@ #include "sysbus.h" -/* Configuration for arm_gic.c: - * how to ID current CPU - */ -static inline int -gic_get_current_cpu(void) -{ - return cpu_single_env->cpu_index; -} - #include "arm_gic.c" /* A9MP private memory region. */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 99c18269c1..c4829d8845 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -10,12 +10,6 @@ #include "sysbus.h" #include "qemu-timer.h" -static inline int -gic_get_current_cpu(void) -{ - return cpu_single_env->cpu_index; -} - #include "arm_gic.c" /* MPCore private memory region. */ diff --git a/hw/arm_gic.c b/hw/arm_gic.c index f64a0015f4..df1a34b8df 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -126,6 +126,16 @@ typedef struct gic_state uint32_t num_irq; } gic_state; +static inline int gic_get_current_cpu(gic_state *s) +{ +#if NCPU > 1 + if (s->num_cpu > 1) { + return cpu_single_env->cpu_index; + } +#endif + return 0; +} + /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ static void gic_update(gic_state *s) @@ -285,7 +295,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) int cm; int mask; - cpu = gic_get_current_cpu(); + cpu = gic_get_current_cpu(s); cm = 1 << cpu; if (offset < 0x100) { #ifndef NVIC @@ -420,7 +430,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, int i; int cpu; - cpu = gic_get_current_cpu(); + cpu = gic_get_current_cpu(s); if (offset < 0x100) { #ifdef NVIC goto bad_reg; @@ -582,7 +592,7 @@ static void gic_dist_writel(void *opaque, target_phys_addr_t offset, int irq; int mask; - cpu = gic_get_current_cpu(); + cpu = gic_get_current_cpu(s); irq = value & 0x3ff; switch ((value >> 24) & 3) { case 0: @@ -665,14 +675,14 @@ static uint64_t gic_thiscpu_read(void *opaque, target_phys_addr_t addr, unsigned size) { gic_state *s = (gic_state *)opaque; - return gic_cpu_read(s, gic_get_current_cpu(), addr); + return gic_cpu_read(s, gic_get_current_cpu(s), addr); } static void gic_thiscpu_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { gic_state *s = (gic_state *)opaque; - gic_cpu_write(s, gic_get_current_cpu(), addr, value); + gic_cpu_write(s, gic_get_current_cpu(s), addr, value); } /* Wrappers to read/write the GIC CPU interface for a specific CPU. diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index bdab709798..99ed85b163 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -17,13 +17,6 @@ #define NVIC 1 -/* Only a single "CPU" interface is present. */ -static inline int -gic_get_current_cpu(void) -{ - return 0; -} - static uint32_t nvic_readl(void *opaque, uint32_t offset); static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index 426f540174..ff7ab848e5 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -262,12 +262,6 @@ uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) /********* GIC part *********/ -static inline int -gic_get_current_cpu(void) -{ - return cpu_single_env->cpu_index; -} - #include "arm_gic.c" typedef struct { diff --git a/hw/realview_gic.c b/hw/realview_gic.c index d114242f27..aa780fe47f 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -9,13 +9,6 @@ #include "sysbus.h" -/* Only a single "CPU" interface is present. */ -static inline int -gic_get_current_cpu(void) -{ - return 0; -} - #include "arm_gic.c" typedef struct { From 544d1afa7013fce155f5afbbc24737f2fc0c0f26 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:07 +0000 Subject: [PATCH 06/16] hw/arm_gic: Expose PPI inputs as gpio inputs Expose the Private Peripheral Interrupt inputs as GPIO inputs. The layout of the GPIO array is thus: [0..N-1] SPIs [N..N+31] PPIs for CPU 0 [N+32..N+63] PPIs for CPU 1 ... Treating PPIs as being another kind of input line is in line with the GIC architecture specification, where they are clearly described that way. The 11MPCore TRM is a bit more ambiguous, but there is no practical difference between "set PPI X as pending" and "0->1 transition on a PPI input line configured as edge triggered", and PPIs are always edge triggered, so this change won't affect behaviour. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index df1a34b8df..fabbcc5d63 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -192,20 +192,40 @@ gic_set_pending_private(gic_state *s, int cpu, int irq) /* 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 + * ... + */ gic_state *s = (gic_state *)opaque; - /* The first external input line is internal interrupt 32. */ - irq += GIC_INTERNAL; - if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) + 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, ALL_CPU_MASK); - if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, ALL_CPU_MASK)) { - DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq)); - GIC_SET_PENDING(irq, GIC_TARGET(irq)); + 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, ALL_CPU_MASK); + GIC_CLEAR_LEVEL(irq, cm); } gic_update(s); } @@ -849,7 +869,18 @@ static void gic_init(gic_state *s, int num_irq) num_irq); } - qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL); + i = s->num_irq - GIC_INTERNAL; +#ifndef NVIC + /* 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 * num_cpu); +#endif + 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]); } From 496dbcd1a38c2ae4ada848445e4a1aa758af9f43 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:07 +0000 Subject: [PATCH 07/16] hw/arm_gic: Make the GIC its own sysbus device Compile arm_gic.c as a standalone C file to produce a self contained sysbus GIC device. Support the legacy usage by #include of the .c file by making those users #define LEGACY_INCLUDED_GIC, so we can convert them one by one. Signed-off-by: Peter Maydell Reviewed-by: Evgeny Voevodin --- Makefile.target | 1 + hw/a15mpcore.c | 1 + hw/a9mpcore.c | 1 + hw/arm11mpcore.c | 1 + hw/arm_gic.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- hw/armv7m_nvic.c | 1 + hw/exynos4210_gic.c | 1 + hw/realview_gic.c | 1 + 8 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Makefile.target b/Makefile.target index 1110796e48..0f09180f01 100644 --- a/Makefile.target +++ b/Makefile.target @@ -365,6 +365,7 @@ obj-arm-y += cadence_uart.o obj-arm-y += cadence_ttc.o obj-arm-y += cadence_gem.o obj-arm-y += xilinx_zynq.o zynq_slcr.o +obj-arm-y += arm_gic.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 2e2ed423da..54c0dbf60e 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -20,6 +20,7 @@ #include "sysbus.h" +#define LEGACY_INCLUDED_GIC #include "arm_gic.c" /* A15MP private memory region. */ diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 1d83c37ad5..164a0d359c 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -10,6 +10,7 @@ #include "sysbus.h" +#define LEGACY_INCLUDED_GIC #include "arm_gic.c" /* A9MP private memory region. */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index c4829d8845..e876a0edc8 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -10,6 +10,7 @@ #include "sysbus.h" #include "qemu-timer.h" +#define LEGACY_INCLUDED_GIC #include "arm_gic.c" /* MPCore private memory region. */ diff --git a/hw/arm_gic.c b/hw/arm_gic.c index fabbcc5d63..b54357033b 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -11,6 +11,8 @@ controller, MPCore distributed interrupt controller and ARMv7-M Nested Vectored Interrupt Controller. */ +#include "sysbus.h" + /* Maximum number of possible interrupts, determined by the GIC architecture */ #define GIC_MAXIRQ 1020 /* First 32 are private to each CPU (SGIs and PPIs). */ @@ -112,7 +114,7 @@ typedef struct gic_state int current_pending[NCPU]; #if NCPU > 1 - int num_cpu; + uint32_t num_cpu; #endif MemoryRegion iomem; /* Distributor */ @@ -906,3 +908,51 @@ static void gic_init(gic_state *s, int num_irq) gic_reset(s); register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); } + +#ifndef LEGACY_INCLUDED_GIC + +static int arm_gic_init(SysBusDevice *dev) +{ + /* Device instance init function for the GIC sysbus device */ + int i; + gic_state *s = FROM_SYSBUS(gic_state, dev); + gic_init(s, s->num_cpu, s->num_irq); + /* Distributor */ + sysbus_init_mmio(dev, &s->iomem); + /* cpu interfaces (one for "current cpu" plus one per cpu) */ + for (i = 0; i <= NUM_CPU(s); i++) { + sysbus_init_mmio(dev, &s->cpuiomem[i]); + } + return 0; +} + +static Property arm_gic_properties[] = { + DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + sbc->init = arm_gic_init; + dc->props = arm_gic_properties; + dc->no_user = 1; +} + +static TypeInfo arm_gic_info = { + .name = "arm_gic", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(gic_state), + .class_init = arm_gic_class_init, +}; + +static void arm_gic_register_types(void) +{ + type_register_static(&arm_gic_info); +} + +type_init(arm_gic_register_types) + +#endif diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 99ed85b163..79cf448678 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -16,6 +16,7 @@ #include "exec-memory.h" #define NVIC 1 +#define LEGACY_INCLUDED_GIC static uint32_t nvic_readl(void *opaque, uint32_t offset); static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index ff7ab848e5..a05dab2d2e 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -262,6 +262,7 @@ uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) /********* GIC part *********/ +#define LEGACY_INCLUDED_GIC #include "arm_gic.c" typedef struct { diff --git a/hw/realview_gic.c b/hw/realview_gic.c index aa780fe47f..a3b5a0472b 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -9,6 +9,7 @@ #include "sysbus.h" +#define LEGACY_INCLUDED_GIC #include "arm_gic.c" typedef struct { From 4637a02752f29f1b28c4a4ddb0c168d2d1299227 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:07 +0000 Subject: [PATCH 08/16] hw/a15mpcore: switch to using sysbus GIC Switch the a15mpcore private peripheral region to using the standalone sysbus GIC device. Signed-off-by: Peter Maydell --- hw/a15mpcore.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 54c0dbf60e..5a7b365548 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -20,23 +20,38 @@ #include "sysbus.h" -#define LEGACY_INCLUDED_GIC -#include "arm_gic.c" - /* A15MP private memory region. */ typedef struct A15MPPrivState { - gic_state gic; + SysBusDevice busdev; uint32_t num_cpu; uint32_t num_irq; MemoryRegion container; + DeviceState *gic; } A15MPPrivState; +static void a15mp_priv_set_irq(void *opaque, int irq, int level) +{ + A15MPPrivState *s = (A15MPPrivState *)opaque; + qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); +} + static int a15mp_priv_init(SysBusDevice *dev) { - A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev); + A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev); + SysBusDevice *busdev; - gic_init(&s->gic, s->num_cpu, s->num_irq); + 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", s->num_irq); + qdev_init_nofail(s->gic); + busdev = sysbus_from_qdev(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, a15mp_priv_set_irq, s->num_irq - 32); /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved @@ -47,8 +62,10 @@ static int a15mp_priv_init(SysBusDevice *dev) * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) */ memory_region_init(&s->container, "a15mp-priv-container", 0x8000); - memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); - memory_region_add_subregion(&s->container, 0x2000, &s->gic.cpuiomem[0]); + memory_region_add_subregion(&s->container, 0x1000, + sysbus_mmio_get_region(busdev, 0)); + memory_region_add_subregion(&s->container, 0x2000, + sysbus_mmio_get_region(busdev, 1)); sysbus_init_mmio(dev, &s->container); return 0; @@ -72,7 +89,7 @@ static void a15mp_priv_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); k->init = a15mp_priv_init; dc->props = a15mp_priv_properties; - /* We currently have no savable state outside the common GIC state */ + /* We currently have no savable state */ } static TypeInfo a15mp_priv_info = { From ddd761653b14ffde7ee18d31f0350429f0402ab4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:08 +0000 Subject: [PATCH 09/16] hw/a9mpcore: Switch to using sysbus GIC Switch the a9mpcore to using the sysbus GIC device rather than having the a9mp private memory region device subclass the GIC. Signed-off-by: Peter Maydell --- hw/a9mpcore.c | 60 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 164a0d359c..c2ff74d4b6 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -10,22 +10,19 @@ #include "sysbus.h" -#define LEGACY_INCLUDED_GIC -#include "arm_gic.c" - /* A9MP private memory region. */ typedef struct a9mp_priv_state { - gic_state gic; + SysBusDevice busdev; uint32_t scu_control; uint32_t scu_status; uint32_t old_timer_status[8]; uint32_t num_cpu; - qemu_irq *timer_irq; MemoryRegion scu_iomem; MemoryRegion ptimer_iomem; MemoryRegion container; DeviceState *mptimer; + DeviceState *gic; uint32_t num_irq; } a9mp_priv_state; @@ -114,18 +111,9 @@ static const MemoryRegionOps a9_scu_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void a9mpcore_timer_irq_handler(void *opaque, int irq, int level) -{ - a9mp_priv_state *s = (a9mp_priv_state *)opaque; - if (level && !s->old_timer_status[irq]) { - gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1)); - } - s->old_timer_status[irq] = level; -} - static void a9mp_priv_reset(DeviceState *dev) { - a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, sysbus_from_qdev(dev)); + a9mp_priv_state *s = FROM_SYSBUS(a9mp_priv_state, sysbus_from_qdev(dev)); int i; s->scu_control = 0; for (i = 0; i < ARRAY_SIZE(s->old_timer_status); i++) { @@ -133,13 +121,29 @@ static void a9mp_priv_reset(DeviceState *dev) } } +static void a9mp_priv_set_irq(void *opaque, int irq, int level) +{ + a9mp_priv_state *s = (a9mp_priv_state *)opaque; + qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); +} + static int a9mp_priv_init(SysBusDevice *dev) { - a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, dev); - SysBusDevice *busdev; + a9mp_priv_state *s = FROM_SYSBUS(a9mp_priv_state, dev); + SysBusDevice *busdev, *gicbusdev; int i; - gic_init(&s->gic, s->num_cpu, s->num_irq); + 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", s->num_irq); + qdev_init_nofail(s->gic); + gicbusdev = sysbus_from_qdev(s->gic); + + /* Pass through outbound IRQ lines from the GIC */ + sysbus_pass_irq(dev, gicbusdev); + + /* Pass through inbound GPIO lines to the GIC */ + qdev_init_gpio_in(&s->busdev.qdev, a9mp_priv_set_irq, s->num_irq - 32); s->mptimer = qdev_create(NULL, "arm_mptimer"); qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); @@ -161,7 +165,8 @@ static int a9mp_priv_init(SysBusDevice *dev) memory_region_init_io(&s->scu_iomem, &a9_scu_ops, s, "a9mp-scu", 0x100); memory_region_add_subregion(&s->container, 0, &s->scu_iomem); /* GIC CPU interface */ - memory_region_add_subregion(&s->container, 0x100, &s->gic.cpuiomem[0]); + memory_region_add_subregion(&s->container, 0x100, + sysbus_mmio_get_region(gicbusdev, 1)); /* Note that the A9 exposes only the "timer/watchdog for this core" * memory region, not the "timer/watchdog for core X" ones 11MPcore has. */ @@ -169,15 +174,20 @@ static int a9mp_priv_init(SysBusDevice *dev) sysbus_mmio_get_region(busdev, 0)); memory_region_add_subregion(&s->container, 0x620, sysbus_mmio_get_region(busdev, 1)); - memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); + memory_region_add_subregion(&s->container, 0x1000, + sysbus_mmio_get_region(gicbusdev, 0)); sysbus_init_mmio(dev, &s->container); - /* Wire up the interrupt from each watchdog and timer. */ - s->timer_irq = qemu_allocate_irqs(a9mpcore_timer_irq_handler, - s, (s->num_cpu + 1) * 2); - for (i = 0; i < s->num_cpu * 2; i++) { - sysbus_connect_irq(busdev, i, s->timer_irq[i]); + /* Wire up the interrupt from each watchdog and timer. + * For each core the timer is PPI 29 and the watchdog PPI 30. + */ + for (i = 0; i < s->num_cpu; i++) { + int ppibase = (s->num_irq - 32) + i * 32; + sysbus_connect_irq(busdev, i * 2, + qdev_get_gpio_in(s->gic, ppibase + 29)); + sysbus_connect_irq(busdev, i * 2 + 1, + qdev_get_gpio_in(s->gic, ppibase + 30)); } return 0; } From fbbd05dc2ad0965d3ffd7556b8af8a74b156f9f9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:08 +0000 Subject: [PATCH 10/16] hw/realview_gic: switch to sysbus GIC Switch the realview_gic device to the standalone sysbus GIC. Signed-off-by: Peter Maydell --- hw/realview_gic.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/hw/realview_gic.c b/hw/realview_gic.c index a3b5a0472b..5bc37a7120 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -9,31 +9,45 @@ #include "sysbus.h" -#define LEGACY_INCLUDED_GIC -#include "arm_gic.c" - typedef struct { - gic_state gic; + SysBusDevice busdev; + DeviceState *gic; MemoryRegion container; } RealViewGICState; -static void realview_gic_map_setup(RealViewGICState *s) +static void realview_gic_set_irq(void *opaque, int irq, int level) { - memory_region_init(&s->container, "realview-gic-container", 0x2000); - memory_region_add_subregion(&s->container, 0, &s->gic.cpuiomem[0]); - memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); + 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_SYSBUSGIC(RealViewGICState, 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. */ - gic_init(&s->gic, 1, 96); - realview_gic_map_setup(s); + 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 = sysbus_from_qdev(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; } From 23b92f60280fd250fbe421727ecfe15676d9f6ca Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:08 +0000 Subject: [PATCH 11/16] hw/exynos4210_gic: Convert to using sysbus GIC Convert the Exynos GIC code to use the standalone sysbus GIC device. Signed-off-by: Peter Maydell Reviewed-by: Evgeny Voevodin --- hw/exynos4210_gic.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index a05dab2d2e..e1b215eff0 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -262,28 +262,44 @@ uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) /********* GIC part *********/ -#define LEGACY_INCLUDED_GIC -#include "arm_gic.c" - typedef struct { - gic_state gic; + 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_SYSBUSGIC(Exynos4210GicState, 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; - gic_init(&s->gic, s->num_cpu, EXYNOS4210_GIC_NIRQ); + 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 = sysbus_from_qdev(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); @@ -295,7 +311,7 @@ static int exynos4210_gic_init(SysBusDevice *dev) sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); memory_region_init_alias(&s->cpu_alias[i], cpu_alias_name, - &s->gic.cpuiomem[0], + sysbus_mmio_get_region(busdev, 1), 0, EXYNOS4210_GIC_CPU_REGION_SIZE); memory_region_add_subregion(&s->cpu_container, @@ -305,7 +321,7 @@ static int exynos4210_gic_init(SysBusDevice *dev) sprintf(dist_alias_name, "%s%x", dist_prefix, i); memory_region_init_alias(&s->dist_alias[i], dist_alias_name, - &s->gic.iomem, + sysbus_mmio_get_region(busdev, 0), 0, EXYNOS4210_GIC_DIST_REGION_SIZE); memory_region_add_subregion(&s->dist_container, From 2e9dfe20a62d93f145ea12a1b4755bb9cd61c327 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:08 +0000 Subject: [PATCH 12/16] hw/arm11mpcore: Convert to using sysbus GIC device Convert arm11mpcore to using the standalone sysbus GIC device. Signed-off-by: Peter Maydell --- hw/arm11mpcore.c | 49 +++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index e876a0edc8..c528d7aa01 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -10,21 +10,18 @@ #include "sysbus.h" #include "qemu-timer.h" -#define LEGACY_INCLUDED_GIC -#include "arm_gic.c" - /* MPCore private memory region. */ typedef struct mpcore_priv_state { - gic_state gic; + SysBusDevice busdev; uint32_t scu_control; int iomemtype; uint32_t old_timer_status[8]; uint32_t num_cpu; - qemu_irq *timer_irq; MemoryRegion iomem; MemoryRegion container; DeviceState *mptimer; + DeviceState *gic; uint32_t num_irq; } mpcore_priv_state; @@ -74,18 +71,16 @@ static const MemoryRegionOps mpcore_scu_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void mpcore_timer_irq_handler(void *opaque, int irq, int level) +static void mpcore_priv_set_irq(void *opaque, int irq, int level) { mpcore_priv_state *s = (mpcore_priv_state *)opaque; - if (level && !s->old_timer_status[irq]) { - gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1)); - } - s->old_timer_status[irq] = level; + qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } static void mpcore_priv_map_setup(mpcore_priv_state *s) { int i; + SysBusDevice *gicbusdev = sysbus_from_qdev(s->gic); SysBusDevice *busdev = sysbus_from_qdev(s->mptimer); memory_region_init(&s->container, "mpcode-priv-container", 0x2000); memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100); @@ -95,31 +90,47 @@ static void mpcore_priv_map_setup(mpcore_priv_state *s) */ for (i = 0; i < (s->num_cpu + 1); i++) { target_phys_addr_t offset = 0x100 + (i * 0x100); - memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]); + memory_region_add_subregion(&s->container, offset, + sysbus_mmio_get_region(gicbusdev, i + 1)); } /* Add the regions for timer and watchdog for "current CPU" and * for each specific CPU. */ - s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler, - s, (s->num_cpu + 1) * 2); for (i = 0; i < (s->num_cpu + 1) * 2; i++) { /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */ target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20; memory_region_add_subregion(&s->container, offset, sysbus_mmio_get_region(busdev, i)); } - memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); - /* Wire up the interrupt from each watchdog and timer. */ - for (i = 0; i < s->num_cpu * 2; i++) { - sysbus_connect_irq(busdev, i, s->timer_irq[i]); + memory_region_add_subregion(&s->container, 0x1000, + sysbus_mmio_get_region(gicbusdev, 0)); + /* Wire up the interrupt from each watchdog and timer. + * For each core the timer is PPI 29 and the watchdog PPI 30. + */ + for (i = 0; i < s->num_cpu; i++) { + int ppibase = (s->num_irq - 32) + i * 32; + sysbus_connect_irq(busdev, i * 2, + qdev_get_gpio_in(s->gic, ppibase + 29)); + sysbus_connect_irq(busdev, i * 2 + 1, + qdev_get_gpio_in(s->gic, ppibase + 30)); } } static int mpcore_priv_init(SysBusDevice *dev) { - mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); + mpcore_priv_state *s = FROM_SYSBUS(mpcore_priv_state, dev); + + 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", s->num_irq); + qdev_init_nofail(s->gic); + + /* Pass through outbound IRQ lines from the GIC */ + sysbus_pass_irq(dev, sysbus_from_qdev(s->gic)); + + /* Pass through inbound GPIO lines to the GIC */ + qdev_init_gpio_in(&s->busdev.qdev, mpcore_priv_set_irq, s->num_irq - 32); - gic_init(&s->gic, s->num_cpu, s->num_irq); s->mptimer = qdev_create(NULL, "arm_mptimer"); qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); qdev_init_nofail(s->mptimer); From aecff6924dab0197b6c8f132e44502b25fd98a38 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:09 +0000 Subject: [PATCH 13/16] hw/arm_gic: Make gic_reset a sysbus reset function Make gic_reset a sysbus reset function, so we actually reset the GIC on system reset rather than only at init. For the NVIC this requires us also to implement reset of the SysTick. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 5 +++-- hw/armv7m_nvic.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index b54357033b..81858c3971 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -741,8 +741,9 @@ static const MemoryRegionOps gic_cpu_ops = { }; #endif -static void gic_reset(gic_state *s) +static void gic_reset(DeviceState *dev) { + gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); int i; memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < NUM_CPU(s); i++) { @@ -905,7 +906,6 @@ static void gic_init(gic_state *s, int num_irq) } #endif - gic_reset(s); register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); } @@ -938,6 +938,7 @@ static void arm_gic_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); sbc->init = arm_gic_init; dc->props = arm_gic_properties; + dc->reset = gic_reset; dc->no_user = 1; } diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 79cf448678..5cfa971cab 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -76,6 +76,14 @@ static void systick_timer_tick(void * opaque) } } +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) @@ -371,6 +379,13 @@ static const VMStateDescription vmstate_nvic = { } }; +static void armv7m_nvic_reset(DeviceState *dev) +{ + nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); + gic_reset(&s->gic.busdev.qdev); + systick_reset(s); +} + static int armv7m_nvic_init(SysBusDevice *dev) { nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); @@ -400,6 +415,7 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; + dc->reset = armv7m_nvic_reset; dc->props = armv7m_nvic_properties; } From 0d256bdc8f540a52fe1f0475aeeed3bc9f6e2de4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:09 +0000 Subject: [PATCH 14/16] hw/arm_gic: Use NVIC instead of LEGACY_INCLUDED_GIC define Now all the A profile cores have been switched to use the standalone sysbus GIC, the only remaining code which #includes arm_gic.c is the v7M NVIC. The coupling is much closer here so it's not so easily disentangled. For now, add a comment about how arm_gic.c is compiled, and assume that the NVIC always includes arm_gic.c and the non-NVIC GIC is always compiled standalone. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 13 ++++++++++--- hw/armv7m_nvic.c | 1 - 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 81858c3971..589ac5ef17 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -8,8 +8,15 @@ */ /* This file contains implementation code for the RealView EB interrupt - controller, MPCore distributed interrupt controller and ARMv7-M - Nested Vectored Interrupt Controller. */ + * 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 "sysbus.h" @@ -909,7 +916,7 @@ static void gic_init(gic_state *s, int num_irq) register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); } -#ifndef LEGACY_INCLUDED_GIC +#ifndef NVIC static int arm_gic_init(SysBusDevice *dev) { diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 5cfa971cab..986a6bbd0c 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -16,7 +16,6 @@ #include "exec-memory.h" #define NVIC 1 -#define LEGACY_INCLUDED_GIC static uint32_t nvic_readl(void *opaque, uint32_t offset); static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); From b7dc1a597ab2d755c135852ce22d98fa00ab414f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:09 +0000 Subject: [PATCH 15/16] hw/arm_gic: gic_set_pending_private() is NVIC only The function gic_set_pending_private() is now used by the NVIC only (for the GIC we now set PPI interrupts via gpio lines and gic_set_irq()). So make it #ifdef NVIC and remove the 'attribute unused' annotation. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 589ac5ef17..ba6117adbd 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -185,8 +185,8 @@ static void gic_update(gic_state *s) } } -static void __attribute__((unused)) -gic_set_pending_private(gic_state *s, int cpu, int irq) +#ifdef NVIC +static void gic_set_pending_private(gic_state *s, int cpu, int irq) { int cm = 1 << cpu; @@ -197,6 +197,7 @@ gic_set_pending_private(gic_state *s, int cpu, int irq) GIC_SET_PENDING(irq, cm); gic_update(s); } +#endif /* Process a change in an external IRQ input. */ static void gic_set_irq(void *opaque, int irq, int level) From c79981ceec3ae1e712aa9c21cba94c152eea2fb5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Apr 2012 11:39:09 +0000 Subject: [PATCH 16/16] hw/arm_gic: Remove stray hardcoded tab Remove the single instance of a hardcoded tab from hw/arm_gic.c. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index ba6117adbd..72298b4b41 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -160,7 +160,7 @@ static void gic_update(gic_state *s) cm = 1 << cpu; s->current_pending[cpu] = 1023; if (!s->enabled || !s->cpu_enabled[cpu]) { - qemu_irq_lower(s->parent_irq[cpu]); + qemu_irq_lower(s->parent_irq[cpu]); return; } best_prio = 0x100;