2013/3/2 Peter Crosthwaite <peter.crosthwa...@xilinx.com>: > 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; >> + 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); >> + } 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? >
Sure, the ftintc020 is going to be redesigned with the 'hw/pl190.c' as template. And all the coding style issues would be updated. >> + /* >> + * 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) > > I'm not sure this is the right place for this, I think device creation > helpers belong on the machine / SoC level. Keep the device model self > contained and clients call qdev_init themselves. Andreas have the > rules change on this recently? > >> +{ >> + int i; >> + DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020); > > As the device is intended for use in an SoC, the SoC potentially needs > to hold a pointer to it in order to call device destructors. This > function should return ds for future use by the instantiator. > >> + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); >> + Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev)); >> + > > Use an Object cast macro. Andreas, can we make this easier for > reviewers and developers by adding blacklisted identifiers to > checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc? > >> + s->cpu = cpu; > > Im thinking this is a QOM link. Its a connection from one device to > another and the machine should be aware of it. > Sorry, I can't get you well.... Did you mean adding the QOM link to cpu like this? ......... s->cpu = cpu_arm_init(s->cpu_model); if (!s->cpu) { hw_error("a369: Unable to find CPU definition\n"); exit(1); } object_property_add_child(qdev_get_machine(), "cpu", OBJECT(s->cpu), NULL); ......... However this would lead to an error like this: ** ERROR:qom/object.c:899:object_property_add_child: assertion failed: (child->parent == NULL) >> + ftintc020_reset(ds); > > Doesn't look right but this is just the already registered > DeviceClass::reset function anyways. You should just be able to delete > this. Does your system function without it? > >> + qdev_init_nofail(ds); > > Cutting from here ... > >> + qdev_init_gpio_in(ds, ftintc020_set_irq, 64); >> + for (i = 0; i < 64; ++i) { >> + s->irqs[i] = qdev_get_gpio_in(ds, i); >> + } >> + >> + /* Enable IC memory-mapped registers access. */ >> + memory_region_init_io(&s->iomem, >> + &mmio_ops, >> + s, >> + TYPE_FTINTC020, >> + 0x1000); >> + sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem); >> + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base); >> + > > ... Im pritty sure all of this belongs in either the ObjectClass::init > or Device::realize functions. > >> + return s->irqs; >> +} >> + >> +static VMStateDescription vmstate_ftintc020 = { >> + .name = TYPE_FTINTC020, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .minimum_version_id_old = 1, >> + .fields = (VMStateField[]) { >> + VMSTATE_UINT32_ARRAY(irq_src, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(irq_ena, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(irq_mod, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(irq_lvl, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(fiq_src, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(fiq_ena, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(fiq_mod, Ftintc020State, 2), >> + VMSTATE_UINT32_ARRAY(fiq_lvl, Ftintc020State, 2), >> + VMSTATE_END_OF_LIST(), >> + }, >> +}; >> + >> +static int ftintc020_initfn(SysBusDevice *dev) >> +{ >> + return 0; >> +} >> + >> +static void ftintc020_class_init(ObjectClass *klass, void *data) >> +{ >> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + k->init = ftintc020_initfn; > > SysBusDevice::init is deprecated in favour of the Device::init. Your > SBD::init doesnt do anything so you can just delete it. But you should > add a device Init here to bring the sysbus foo from your helper > instantiator into the device model. > > Regards, > Peter > >> + dc->vmsd = &vmstate_ftintc020; >> + dc->reset = ftintc020_reset; >> + dc->no_user = 1; >> +} >> + >> +static const TypeInfo ftintc020_info = { >> + .name = TYPE_FTINTC020, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(Ftintc020State), >> + .class_init = ftintc020_class_init, >> +}; >> + >> +static void ftintc020_register_types(void) >> +{ >> + type_register_static(&ftintc020_info); >> +} >> + >> +type_init(ftintc020_register_types) >> diff --git a/hw/arm/ftintc020.h b/hw/arm/ftintc020.h >> new file mode 100644 >> index 0000000..f17fb58 >> --- /dev/null >> +++ b/hw/arm/ftintc020.h >> @@ -0,0 +1,48 @@ >> +/* >> + * 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+. >> + */ >> + >> +#ifndef HW_ARM_FTINTC020_H >> +#define HW_ARM_FTINTC020_H >> + >> +/* >> + * IRQ/FIO: 0 ~ 31 >> + */ >> +#define REG_IRQSRC 0x00 /* IRQ source register */ >> +#define REG_IRQENA 0x04 /* IRQ enable register */ >> +#define REG_IRQSCR 0x08 /* IRQ status clear register */ >> +#define REG_IRQMDR 0x0C /* IRQ trigger mode register */ >> +#define REG_IRQLVR 0x10 /* IRQ trigger level register */ >> +#define REG_IRQSR 0x14 /* IRQ status register */ >> + >> +#define REG_FIQSRC 0x20 /* FIQ source register */ >> +#define REG_FIQENA 0x24 /* FIQ enable register */ >> +#define REG_FIQSCR 0x28 /* FIQ status clear register */ >> +#define REG_FIQMDR 0x2C /* FIQ trigger mode register */ >> +#define REG_FIQLVR 0x30 /* FIQ trigger level register */ >> +#define REG_FIQSR 0x34 /* FIQ status register */ >> + >> +/* >> + * Extended IRQ/FIO: 32 ~ 63 >> + */ >> + >> +#define REG_EIRQSRC 0x60 /* Extended IRQ source register */ >> +#define REG_EIRQENA 0x64 /* Extended IRQ enable register */ >> +#define REG_EIRQSCR 0x68 /* Extended IRQ status clear register */ >> +#define REG_EIRQMDR 0x6C /* Extended IRQ trigger mode register */ >> +#define REG_EIRQLVR 0x70 /* Extended IRQ trigger level register */ >> +#define REG_EIRQSR 0x74 /* Extended IRQ status register */ >> + >> +#define REG_EFIQSRC 0x80 /* Extended FIQ source register */ >> +#define REG_EFIQENA 0x84 /* Extended FIQ enable register */ >> +#define REG_EFIQSCR 0x88 /* Extended FIQ status clear register */ >> +#define REG_EFIQMDR 0x8C /* Extended FIQ trigger mode register */ >> +#define REG_EFIQLVR 0x90 /* Extended FIQ trigger level register */ >> +#define REG_EFIQSR 0x94 /* Extended FIQ status register */ >> + >> +#endif >> -- >> 1.7.9.5 >> >> -- Best wishes, Kuo-Jung Su