2013/3/3 Peter Crosthwaite <peter.crosthwa...@xilinx.com>: > Hi Kuo-Jung, > > On Sat, Mar 2, 2013 at 2:13 PM, Peter Crosthwaite > <peter.crosthwa...@xilinx.com> wrote: >> Hi Kuo-Jung, >> >> On Wed, Feb 27, 2013 at 5:15 PM, Kuo-Jung Su <dant...@gmail.com> wrote: >>> From: Kuo-Jung Su <dant...@faraday-tech.com> >>> >>> The FTINTC020 interrupt controller supports both FIQ and IRQ signals >>> to the microprocessor. >>> It can handle up to 64 configurable IRQ sources and 64 FIQ sources. >>> The output signals to the microprocessor can be configured as >>> level-high/low active or edge-rising/falling triggered. >>> >>> Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> >>> --- >>> hw/arm/Makefile.objs | 1 + >>> hw/arm/faraday.h | 3 + >>> hw/arm/faraday_a369_soc.c | 10 +- >>> hw/arm/ftintc020.c | 366 >>> +++++++++++++++++++++++++++++++++++++++++++++ >>> hw/arm/ftintc020.h | 48 ++++++ >>> 5 files changed, 425 insertions(+), 3 deletions(-) >>> create mode 100644 hw/arm/ftintc020.c >>> create mode 100644 hw/arm/ftintc020.h >>> >>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs >>> index f6fd60d..6771072 100644 >>> --- a/hw/arm/Makefile.objs >>> +++ b/hw/arm/Makefile.objs >>> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \ >>> faraday_a369_soc.o \ >>> faraday_a369_scu.o \ >>> faraday_a369_kpd.o >>> +obj-y += ftintc020.o >>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h >>> index d6ed860..e5f611d 100644 >>> --- a/hw/arm/faraday.h >>> +++ b/hw/arm/faraday.h >>> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState { >>> FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \ >>> TYPE_FARADAY_SOC)) >>> >>> +/* ftintc020.c */ >>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu); >>> + >>> #endif >>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c >>> index 0372868..3d861d2 100644 >>> --- a/hw/arm/faraday_a369_soc.c >>> +++ b/hw/arm/faraday_a369_soc.c >>> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s) >>> { >>> DriveInfo *dinfo; >>> DeviceState *ds; >>> + qemu_irq *pic; >>> >>> s->as = get_system_memory(); >>> s->ram = g_new(MemoryRegion, 1); >>> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s) >>> exit(1); >>> } >>> >>> + /* Interrupt Controller */ >>> + pic = ftintc020_init(0x90100000, s->cpu); >>> + >>> /* Serial (FTUART010 which is 16550A compatible) */ >>> if (serial_hds[0]) { >>> serial_mm_init(s->as, >>> 0x92b00000, >>> 2, >>> - NULL, >>> + pic[53], >>> 18432000, >>> serial_hds[0], >>> DEVICE_LITTLE_ENDIAN); >>> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s) >>> serial_mm_init(s->as, >>> 0x92c00000, >>> 2, >>> - NULL, >>> + pic[54], >>> 18432000, >>> serial_hds[1], >>> DEVICE_LITTLE_ENDIAN); >>> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s) >>> s->scu = ds; >>> >>> /* ftkbc010 */ >>> - sysbus_create_simple("a369.keypad", 0x92f00000, NULL); >>> + sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]); >>> } >>> >>> static int a369soc_init(SysBusDevice *busdev) >>> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c >>> new file mode 100644 >>> index 0000000..a7f6454 >>> --- /dev/null >>> +++ b/hw/arm/ftintc020.c >>> @@ -0,0 +1,366 @@ >>> +/* >>> + * Faraday FTINTC020 Programmable Interrupt Controller. >>> + * >>> + * Copyright (c) 2012 Faraday Technology >>> + * Written by Dante Su <dant...@faraday-tech.com> >>> + * >>> + * This code is licensed under GNU GPL v2+. >>> + */ >>> + >>> +#include "hw/hw.h" >>> +#include "hw/sysbus.h" >>> + >>> +#include "faraday.h" >>> +#include "ftintc020.h" >>> + >>> +#define TYPE_FTINTC020 "ftintc020" >>> + >>> +typedef struct Ftintc020State { >>> + SysBusDevice busdev; >>> + MemoryRegion iomem; >>> + ARMCPU *cpu; > > So Ive looked into your init routine problem a little more and > checkout out how its handled by other ARM interrupt controllers. I > think its gone wrong here, in that an interrupt controller should not > have a handle to a CPU at all. It should just have GPIO outputs for > the interrupt wires. Replace this with GPIO outputs for your intcs IRQ > and FIQ output. This removes the need for your machine model to pass > in an ARMCPU to the device (whether that be via your Ad-Hoc creation > helper or via a QOM link). >
At the time I was writing the model for FTINTC020, I found that hw/pxa2xx_pic.c is more straightforward to me. However it turns out that I've made a bad decision. >>> + qemu_irq irqs[64]; >>> + >>> + uint32_t irq_pin[2]; /* IRQ pin state */ >>> + uint32_t fiq_pin[2]; /* IRQ pin state */ >>> + >>> + /* HW register caches */ >>> + uint32_t irq_src[2]; /* IRQ source register */ >>> + uint32_t irq_ena[2]; /* IRQ enable register */ >>> + uint32_t irq_mod[2]; /* IRQ mode register */ >>> + uint32_t irq_lvl[2]; /* IRQ level register */ >>> + uint32_t fiq_src[2]; /* FIQ source register */ >>> + uint32_t fiq_ena[2]; /* FIQ enable register */ >>> + uint32_t fiq_mod[2]; /* FIQ mode register */ >>> + uint32_t fiq_lvl[2]; /* FIQ level register */ >>> +} Ftintc020State; >>> + >>> +#define FTINTC020(obj) \ >>> + OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020) >>> + >>> +static void >>> +ftintc020_update(Ftintc020State *s) >>> +{ >>> + uint32_t mask[2]; >>> + >>> + /* FIQ */ >>> + mask[0] = s->fiq_src[0] & s->fiq_ena[0]; >>> + mask[1] = s->fiq_src[1] & s->fiq_ena[1]; >>> + >>> + if (mask[0] || mask[1]) { >>> + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ); > > Access to the cpu->env from device land is generally bad. Replacing > this logic with GPIO outputs would remove usage of the env. > >>> + } else { >>> + cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ); >>> + } >>> + >>> + /* IRQ */ >>> + mask[0] = s->irq_src[0] & s->irq_ena[0]; >>> + mask[1] = s->irq_src[1] & s->irq_ena[1]; >>> + >>> + if (mask[0] || mask[1]) { >>> + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); >>> + } else { >>> + cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); >>> + } >>> +} >>> + >>> +/* Note: Here level means state of the signal on a pin */ >>> +static void >>> +ftintc020_set_irq(void *opaque, int irq, int level) >>> +{ >>> + Ftintc020State *s = FTINTC020(opaque); >>> + uint32_t i = irq / 32; >>> + uint32_t mask = 1 << (irq % 32); >>> + >>> + if (s->irq_mod[i] & mask) { >>> + /* Edge Triggered */ >>> + if (s->irq_lvl[i] & mask) { >>> + /* Falling Active */ >>> + if ((s->irq_pin[i] & mask) && !level) { >>> + s->irq_src[i] |= mask; >>> + s->fiq_src[i] |= mask; >>> + } >>> + } else { >>> + /* Rising Active */ >>> + if (!(s->irq_pin[i] & mask) && level) { >>> + s->irq_src[i] |= mask; >>> + s->fiq_src[i] |= mask; >>> + } >>> + } >>> + } else { >>> + /* Level Triggered */ >>> + if (s->irq_lvl[i] & mask) { >>> + /* Low Active */ >>> + if (level) { >>> + s->irq_src[i] &= ~mask; >>> + s->fiq_src[i] &= ~mask; >>> + } else { >>> + s->irq_src[i] |= mask; >>> + s->fiq_src[i] |= mask; >>> + } >>> + } else { >>> + /* High Active */ >>> + if (!level) { >>> + s->irq_src[i] &= ~mask; >>> + s->fiq_src[i] &= ~mask; >>> + } else { >>> + s->irq_src[i] |= mask; >>> + s->fiq_src[i] |= mask; >>> + } >>> + } >>> + } >>> + >>> + /* update IRQ/FIQ pin states */ >>> + if (level) { >>> + s->irq_pin[i] |= mask; >>> + s->fiq_pin[i] |= mask; >>> + } else { >>> + s->irq_pin[i] &= ~mask; >>> + s->fiq_pin[i] &= ~mask; >>> + } >>> + >>> + ftintc020_update(s); >>> +} >>> + >>> +static uint64_t >>> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size) >>> +{ >>> + Ftintc020State *s = FTINTC020(opaque); >>> + >>> + switch (addr) { >>> + /* >>> + * IRQ >>> + */ >>> + case REG_IRQSRC: >>> + return s->irq_src[0]; >>> + case REG_IRQENA: >>> + return s->irq_ena[0]; >>> + case REG_IRQMDR: >>> + return s->irq_mod[0]; >>> + case REG_IRQLVR: >>> + return s->irq_lvl[0]; >>> + case REG_IRQSR: >>> + return s->irq_src[0] & s->irq_ena[0]; >>> + case REG_EIRQSRC: >>> + return s->irq_src[1]; >>> + case REG_EIRQENA: >>> + return s->irq_ena[1]; >>> + case REG_EIRQMDR: >>> + return s->irq_mod[1]; >>> + case REG_EIRQLVR: >>> + return s->irq_lvl[1]; >>> + case REG_EIRQSR: >>> + return s->irq_src[1] & s->irq_ena[1]; >>> + >> >> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ. >> Can you #define some symbols accrordingly and remove all the magic >> numberage with the [0]'s and [1]'s? >> >>> + /* >>> + * FIQ >>> + */ >>> + case REG_FIQSRC: >>> + return s->fiq_src[0]; >>> + case REG_FIQENA: >>> + return s->fiq_ena[0]; >>> + case REG_FIQMDR: >>> + return s->fiq_mod[0]; >>> + case REG_FIQLVR: >>> + return s->fiq_lvl[0]; >>> + case REG_FIQSR: >>> + return s->fiq_src[0] & s->fiq_ena[0]; >>> + case REG_EFIQSRC: >>> + return s->fiq_src[1]; >>> + case REG_EFIQENA: >>> + return s->fiq_ena[1]; >>> + case REG_EFIQMDR: >>> + return s->fiq_mod[1]; >>> + case REG_EFIQLVR: >>> + return s->fiq_lvl[1]; >>> + case REG_EFIQSR: >>> + return s->fiq_src[1] & s->fiq_ena[1]; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "ftintc020: undefined memory access@0x%llx\n", addr); >>> + return 0; >>> + } >>> +} >>> + >>> +static void >>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned >>> size) >>> +{ >>> + Ftintc020State *s = FTINTC020(opaque); >>> + >>> + switch (addr) { >>> + /* >>> + * IRQ >>> + */ >> >> Ok to use one line comment. And elsewhere >> >>> + case REG_IRQENA: >>> + s->irq_ena[0] = (uint32_t)value; >>> + break; >>> + case REG_IRQSCR: >>> + value = ~(value & s->irq_mod[0]); >>> + s->irq_src[0] &= (uint32_t)value; >>> + break; >>> + case REG_IRQMDR: >>> + s->irq_mod[0] = (uint32_t)value; >>> + break; >>> + case REG_IRQLVR: >>> + s->irq_lvl[0] = (uint32_t)value; >>> + break; >>> + case REG_EIRQENA: >>> + s->irq_ena[1] = (uint32_t)value; >>> + break; >>> + case REG_EIRQSCR: >>> + value = ~(value & s->irq_mod[1]); >>> + s->irq_src[1] &= (uint32_t)value; >>> + break; >>> + case REG_EIRQMDR: >>> + s->irq_mod[1] = (uint32_t)value; >>> + break; >>> + case REG_EIRQLVR: >>> + s->irq_lvl[1] = (uint32_t)value; >>> + break; >>> + >>> + /* >>> + * FIQ >>> + */ >>> + case REG_FIQENA: >>> + s->fiq_ena[0] = (uint32_t)value; >>> + break; >>> + case REG_FIQSCR: >>> + value = ~(value & s->fiq_mod[0]); >>> + s->fiq_src[0] &= (uint32_t)value; >>> + break; >>> + case REG_FIQMDR: >>> + s->fiq_mod[0] = (uint32_t)value; >>> + break; >>> + case REG_FIQLVR: >>> + s->fiq_lvl[0] = (uint32_t)value; >>> + break; >>> + case REG_EFIQENA: >>> + s->fiq_ena[1] = (uint32_t)value; >>> + break; >>> + case REG_EFIQSCR: >>> + value = ~(value & s->fiq_mod[1]); >>> + s->fiq_src[1] &= (uint32_t)value; >>> + break; >>> + case REG_EFIQMDR: >>> + s->fiq_mod[1] = (uint32_t)value; >>> + break; >>> + case REG_EFIQLVR: >>> + s->fiq_lvl[1] = (uint32_t)value; >>> + break; >>> + >>> + /* don't care */ >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "ftintc020: undefined memory access@0x%llx\n", addr); >>> + return; >>> + } >>> + >>> + ftintc020_update(s); >>> +} >>> + >>> +static const MemoryRegionOps mmio_ops = { >>> + .read = ftintc020_mem_read, >>> + .write = ftintc020_mem_write, >>> + .endianness = DEVICE_LITTLE_ENDIAN, >>> + .valid = { >>> + .min_access_size = 4, >>> + .max_access_size = 4, >>> + } >>> +}; >>> + >>> +static void ftintc020_reset(DeviceState *ds) >>> +{ >>> + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); >>> + Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev)); >>> + >>> + s->irq_pin[0] = 0; >>> + s->irq_pin[1] = 0; >>> + s->fiq_pin[0] = 0; >>> + s->fiq_pin[1] = 0; >>> + >>> + s->irq_src[0] = 0; >>> + s->irq_src[1] = 0; >>> + s->irq_ena[0] = 0; >>> + s->irq_ena[1] = 0; >>> + s->fiq_src[0] = 0; >>> + s->fiq_src[1] = 0; >>> + s->fiq_ena[0] = 0; >>> + s->fiq_ena[1] = 0; >>> + >>> + ftintc020_update(s); >>> +} >>> + >>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu) > > > This problematic function then goes away completely. The > machine-model/SoC just instantiates your intc and connects your GPIO > outputs to the ARM cpu. You will need to add a call to > arm_pic_init_cpu to your machine-model/SoC. Have a look at vexpress.c > for an example of a machine that does this. ARM gic and mpcore both > export FIQ and IRQ as GPIOs which allows for this much cleaner > solution. > Got it, thanks The FTINTC020 has been re-written with vexpress.c, versatilepb.c and pl190.c as templates > Regards, > Peter -- Best wishes, Kuo-Jung Su