From patchwork Fri Jan 25 08:19:54 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: 215579 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 767182C0080 for ; Fri, 25 Jan 2013 19:24:19 +1100 (EST) Received: from localhost ([::1]:59358 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tyea9-00039U-Kn for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 03:24:17 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44817) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeXH-0005tF-RF for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:30 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeXD-0008Iw-GW for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:19 -0500 Received: from mail-pb0-f47.google.com ([209.85.160.47]:47383) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeXD-0008Id-7M for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:15 -0500 Received: by mail-pb0-f47.google.com with SMTP id wz17so79186pbc.6 for ; Fri, 25 Jan 2013 00:21:14 -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:in-reply-to:references; bh=ePSXpk8v/em4MRqI45cGhWSSN6UF1PfunO/UTILSA0Q=; b=0mpcyXfYQdQNKrV0WnxQZZLGcrIx2QhTG41SU7b+Jo+4QvssuEtsNPv/R0LYfeBtIW 8umGIryAZ5QR2qbunHzJarSECWNX4tMx0rS4NA6ke68o7g+WRWgW+wAnR0Mom4P3i3Gl XTDyZGf7ozKneTp37E6XH+Ww9UxjH6naGFeM+zq9R4OStJN2+HymVloegX+cAfmm68ef 9/BYaBio8bnffmc4moZVAqyUrCvNGRQQB8i1X1aQwLmHks5+kN0MJc44TKY508gn8H50 U71hNpA3oQEwFIIwk8Q1sOekdbWzmmShb+c5NVog97s0QZ0i2HOzTz31SrZGR3UUE+bO YnRQ== X-Received: by 10.68.219.164 with SMTP id pp4mr12282173pbc.72.1359102074439; Fri, 25 Jan 2013 00:21:14 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.21.11 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:21:13 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:54 +0800 Message-Id: <1359101996-11667-19-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1359101996-11667-1-git-send-email-dantesu@gmail.com> References: <1359101996-11667-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> References: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.160.47 Cc: peter.maydell@linaro.org, blauwirbel@gmail.com, paul@codesourcery.com, Kuo-Jung Su , afaerber@suse.de, fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v2 18/20] arm: add Faraday FTINTC020 interrupt controller 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. To solve the problem of the interrupt latency, the FTINTC020 supports the AHB interface and 16 vectored IRQ interrupts. The interrupt priority hardware engine supports the fix-order, fix-prior, and round-robin schemes. The priority schemes should be programmable. It also supports the software-triggered interrupts, which are useful in dealing with the inter-processor communication. If the vectored IRQs are not enabled, this controller will be fully compatible to FTINTC010. Signed-off-by: Kuo-Jung Su --- hw/ftintc020.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 hw/ftintc020.c diff --git a/hw/ftintc020.c b/hw/ftintc020.c new file mode 100644 index 0000000..7c5baa2 --- /dev/null +++ b/hw/ftintc020.c @@ -0,0 +1,354 @@ +/* + * Faraday FTINTC020 Programmable Interrupt Controller. + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under the GPL v2. + */ + +#include "hw.h" +#include "sysbus.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 */ +} ftintc020_state; + +#define FTINTC020(obj) \ + OBJECT_CHECK(ftintc020_state, obj, TYPE_FTINTC020) + +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu); + +static void ftintc020_update(ftintc020_state *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) +{ + ftintc020_state *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) +{ + ftintc020_state *s = FTINTC020(opaque); + + switch (offset) { + /* + * IRQ + */ + case 0x0000: /* IRQ source register */ + return s->irq_src[0]; + case 0x0004: /* IRQ enable register */ + return s->irq_ena[0]; + case 0x000C: /* IRQ trigger-mode register */ + return s->irq_mod[0]; + case 0x0010: /* IRQ trigger-level register */ + return s->irq_lvl[0]; + case 0x0014: /* IRQ status register */ + return s->irq_src[0] & s->irq_ena[0]; + case 0x0060: /* IRQ source register */ + return s->irq_src[1]; + case 0x0064: /* IRQ enable register */ + return s->irq_ena[1]; + case 0x006C: /* IRQ trigger-mode register */ + return s->irq_mod[1]; + case 0x0070: /* IRQ trigger-level register */ + return s->irq_lvl[1]; + case 0x0074: /* IRQ status register */ + return s->irq_src[1] & s->irq_ena[1]; + + /* + * FIQ + */ + case 0x0020: /* FIQ source register */ + return s->fiq_src[0]; + case 0x0024: /* FIQ enable register */ + return s->fiq_ena[0]; + case 0x002C: /* FIQ trigger-mode register */ + return s->fiq_mod[0]; + case 0x0030: /* FIQ trigger-level register */ + return s->fiq_lvl[0]; + case 0x0034: /* FIQ status register */ + return s->fiq_src[0] & s->fiq_ena[0]; + case 0x0080: /* FIQ source register */ + return s->fiq_src[1]; + case 0x0084: /* FIQ enable register */ + return s->fiq_ena[1]; + case 0x008C: /* FIQ trigger-mode register */ + return s->fiq_mod[1]; + case 0x0090: /* FIQ trigger-level register */ + return s->fiq_lvl[1]; + case 0x0094: /* FIQ status register */ + 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) +{ + ftintc020_state *s = FTINTC020(opaque); + + switch (offset) { + /* + * IRQ + */ + case 0x0004: /* IRQ enable register */ + s->irq_ena[0] = (uint32_t)value; + break; + case 0x0008: /* IRQ interrupt clear register */ + value = ~(value & s->irq_mod[0]); + s->irq_src[0] &= (uint32_t)value; + break; + case 0x000C: + s->irq_mod[0] = (uint32_t)value; + break; + case 0x0010: + s->irq_lvl[0] = (uint32_t)value; + break; + case 0x0064: /* IRQ enable register */ + s->irq_ena[1] = (uint32_t)value; + break; + case 0x0068: /* IRQ interrupt clear register */ + value = ~(value & s->irq_mod[1]); + s->irq_src[1] &= (uint32_t)value; + break; + case 0x006C: + s->irq_mod[1] = (uint32_t)value; + break; + case 0x0070: + s->irq_lvl[1] = (uint32_t)value; + break; + + /* + * FIQ + */ + case 0x0024: /* FIQ enable register */ + s->fiq_ena[0] = (uint32_t)value; + break; + case 0x0028: /* FIQ interrupt clear register */ + value = ~(value & s->fiq_mod[0]); + s->fiq_src[0] &= (uint32_t)value; + break; + case 0x002C: + s->fiq_mod[0] = (uint32_t)value; + break; + case 0x0030: + s->fiq_lvl[0] = (uint32_t)value; + break; + case 0x0084: /* FIQ enable register */ + s->fiq_ena[1] = (uint32_t)value; + break; + case 0x0088: /* FIQ interrupt clear register */ + value = ~(value & s->fiq_mod[1]); + s->fiq_src[1] &= (uint32_t)value; + break; + case 0x008C: + s->fiq_mod[1] = (uint32_t)value; + break; + case 0x0090: + 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); + ftintc020_state *s = FTINTC020(FROM_SYSBUS(ftintc020_state, 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); + ftintc020_state *s = FTINTC020(FROM_SYSBUS(ftintc020_state, 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, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(irq_ena, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(irq_mod, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(irq_lvl, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(fiq_src, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(fiq_ena, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(fiq_mod, ftintc020_state, 2), + VMSTATE_UINT32_ARRAY(fiq_lvl, ftintc020_state, 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(ftintc020_state), + .class_init = ftintc020_class_init, +}; + +static void ftintc020_register_types(void) +{ + type_register_static(&ftintc020_info); +} + +type_init(ftintc020_register_types)