From patchwork Wed Feb 6 09:45:09 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 218518 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 227AB2C02B4 for ; Wed, 6 Feb 2013 20:53:49 +1100 (EST) Received: from localhost ([::1]:46965 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31hL-00075i-BH for incoming@patchwork.ozlabs.org; Wed, 06 Feb 2013 04:53:47 -0500 Received: from eggs.gnu.org ([208.118.235.92]:36974) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31h7-00075F-Kl for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:37 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U31h3-0000VO-QR for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:33 -0500 Received: from mail-ia0-x22d.google.com ([2607:f8b0:4001:c02::22d]:38764) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31h3-0000VF-Kt for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:53:29 -0500 Received: by mail-ia0-f173.google.com with SMTP id h37so1337582iak.4 for ; Wed, 06 Feb 2013 01:53:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=dTQuYCzcQqFRRaNGMMX01GPapWXkvOUHrNtAjrE9An8=; b=dR/NLGOArSVdNX4HQctoSt3dK4bPv+SQxf2fifKZ8qv3HtGzoelhtphJcJFoDPBBSN ObqcA0NPgu0DQuMzdywBsGzIaPrhnSGC559uFtxfDdWsx4X22pOy9XsG0z5OawVJpyZ/ Ytb/5n3xiVpf+QEUl+qBN8CBfCXP/ITvyDQlSuAwEk8CpuV+hJ+h39UoX/YxQjP6mTWj pP1yNfOn1zPQlPuw3ygZ0WlFYkoHQTPNTcAeLaBWEwkVloqd0wsSXo0Z7Uwyj+6eeOH+ MWNWEXFaHk8AXwqlDybUOsAznWxuMY36hXpHSHPJc3TY0rMb2L8CyNuwnkOvadPP8563 g5qQ== X-Received: by 10.50.149.168 with SMTP id ub8mr4497917igb.111.1360144010532; Wed, 06 Feb 2013 01:46:50 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id hg2sm2671279igc.3.2013.02.06.01.46.47 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Wed, 06 Feb 2013 01:46:50 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Wed, 6 Feb 2013 17:45:09 +0800 Message-Id: <1360143925-10800-6-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1360143925-10800-1-git-send-email-dantesu@gmail.com> References: <1360143925-10800-1-git-send-email-dantesu@gmail.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:4001:c02::22d Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v3 05/20] arm: add Faraday FTINTC020 interrupt controller support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Kuo-Jung Su 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 --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a360.c | 9 +- hw/arm/faraday_a369.c | 11 +- hw/arm/ftintc020.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/arm/ftintc020.h | 48 +++++++ 5 files changed, 420 insertions(+), 5 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 fede407..189f777 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -37,3 +37,4 @@ obj-y += faraday_a360.o faraday_a360_pmu.o obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o obj-y += ftahbc020.o obj-y += ftddrii030.o +obj-y += ftintc020.o diff --git a/hw/arm/faraday_a360.c b/hw/arm/faraday_a360.c index a81b6fb..2467016 100644 --- a/hw/arm/faraday_a360.c +++ b/hw/arm/faraday_a360.c @@ -28,12 +28,17 @@ typedef FaradayMachState A360State; static void a360_device_init(A360State *s) { + qemu_irq *pic; + + /* Interrupt Controller */ + pic = ftintc020_init(0x98800000, s->cpu); + /* Serial (FTUART010 which is 16550A compatible) */ if (serial_hds[0]) { serial_mm_init(s->as, 0x98200000, 2, - NULL, + pic[10], 18432000 / 16, serial_hds[0], DEVICE_LITTLE_ENDIAN); @@ -42,7 +47,7 @@ a360_device_init(A360State *s) serial_mm_init(s->as, 0x98300000, 2, - NULL, + pic[11], 18432000 / 16, serial_hds[1], DEVICE_LITTLE_ENDIAN); diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c index f60bf4e..77775b8 100644 --- a/hw/arm/faraday_a369.c +++ b/hw/arm/faraday_a369.c @@ -29,12 +29,17 @@ typedef FaradayMachState A369State; static void a369_device_init(A369State *s) { + qemu_irq *pic; + + /* 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 / 16, serial_hds[0], DEVICE_LITTLE_ENDIAN); @@ -43,7 +48,7 @@ a369_device_init(A369State *s) serial_mm_init(s->as, 0x92c00000, 2, - NULL, + pic[54], 18432000 / 16, serial_hds[1], DEVICE_LITTLE_ENDIAN); @@ -53,7 +58,7 @@ a369_device_init(A369State *s) s->scu = sysbus_create_simple("a369.scu", 0x92000000, NULL); /* ftkbc010 */ - sysbus_create_simple("a369.keypad", 0x92f00000, NULL); + sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]); /* ftahbc020 */ s->ahbc = qdev_create(NULL, "ftahbc020"); diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c new file mode 100644 index 0000000..0850881 --- /dev/null +++ b/hw/arm/ftintc020.c @@ -0,0 +1,356 @@ +/* + * Faraday FTINTC020 Programmable Interrupt Controller. + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under GNU GPL v2+. + */ + +#include +#include + +#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) + +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu); + +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 offset, unsigned size) +{ + Ftintc020State *s = FTINTC020(opaque); + + switch (offset) { + /* + * IRQ + */ + case REG_IRQSRC: + return s->irq_src[0]; + case REG_IRQENR: + 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_EIRQENR: + 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]; + + /* + * FIQ + */ + case REG_FIQSRC: + return s->fiq_src[0]; + case REG_FIQENR: + 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_EFIQENR: + 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: + return 0; + } +} + +static void ftintc020_mem_write(void *opaque, + hwaddr offset, + uint64_t value, + unsigned size) +{ + Ftintc020State *s = FTINTC020(opaque); + + switch (offset) { + /* + * IRQ + */ + case REG_IRQENR: + 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_EIRQENR: + 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_FIQENR: + 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_EFIQENR: + 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; + default: + return; + } + + ftintc020_update(s); +} + +static const MemoryRegionOps ftintc020_mem_ops = { + .read = ftintc020_mem_read, + .write = ftintc020_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +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) +{ + int i; + DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020); + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev)); + + s->cpu = cpu; + ftintc020_reset(ds); + qdev_init_nofail(ds); + 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, + &ftintc020_mem_ops, + s, + TYPE_FTINTC020, + 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem); + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base); + + 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; + 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..6f84226 --- /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 + * + * 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_IRQENR 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_FIQENR 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_EIRQENR 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_EFIQENR 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