From patchwork Tue Sep 9 14:15:09 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Thompson X-Patchwork-Id: 37113 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-yk0-f199.google.com (mail-yk0-f199.google.com [209.85.160.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 125EB20566 for ; Tue, 9 Sep 2014 14:15:35 +0000 (UTC) Received: by mail-yk0-f199.google.com with SMTP id 19sf6105607ykq.10 for ; Tue, 09 Sep 2014 07:15:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=giN0RHt6vYk1PILgexV9UUnguAirEPZAAwwiAWrc07E=; b=jK0XIF9p9S+OvEGRmNtCSjjr6/9OQBDWkGkGPUjR2SQr9l/SO2vnc9XtlM53/CmSWV 32lGsJe8Q035XUpAMt9kb7uAC+1DB+8LHBznzECMOB7apNtMFljqVGHGC7mXzOCtLeUC XhDVZ41bm9fA021WOt/6oezz7B97r//ayG1+J5BH+ZTF9r7bAR4kgJp9pF/aOjTMG0s7 KV44YnsIkdxCmcihJS4TidDHFB40hRYLpSRB8ocyehnxQoljdf49nxl7nv/SP9OzVOBL +YPEpblGbCuzG50wZ/BCBP8m62UghESEQmnHBWDZZpXyVFRK+GrNnqbx+Ny9qVvbak6q QqDw== X-Gm-Message-State: ALoCoQlGE9t9aNIkrYxHRlYyC8U0S+hskqqwpkSCvfpF+WL9LPmNfYMWkVo4RIE7mE+qtrUy8mml X-Received: by 10.224.45.133 with SMTP id e5mr21803173qaf.3.1410272132877; Tue, 09 Sep 2014 07:15:32 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.28.196 with SMTP id 62ls1189007qgz.12.gmail; Tue, 09 Sep 2014 07:15:32 -0700 (PDT) X-Received: by 10.52.26.206 with SMTP id n14mr25558919vdg.0.1410272132718; Tue, 09 Sep 2014 07:15:32 -0700 (PDT) Received: from mail-vc0-f178.google.com (mail-vc0-f178.google.com [209.85.220.178]) by mx.google.com with ESMTPS id qj9si5183215vcb.63.2014.09.09.07.15.32 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 09 Sep 2014 07:15:32 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) client-ip=209.85.220.178; Received: by mail-vc0-f178.google.com with SMTP id hy4so2351697vcb.23 for ; Tue, 09 Sep 2014 07:15:32 -0700 (PDT) X-Received: by 10.52.163.52 with SMTP id yf20mr4690439vdb.40.1410272132631; Tue, 09 Sep 2014 07:15:32 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.221.45.67 with SMTP id uj3csp290825vcb; Tue, 9 Sep 2014 07:15:32 -0700 (PDT) X-Received: by 10.180.187.7 with SMTP id fo7mr30747493wic.58.1410272131208; Tue, 09 Sep 2014 07:15:31 -0700 (PDT) Received: from mail-we0-f174.google.com (mail-we0-f174.google.com [74.125.82.174]) by mx.google.com with ESMTPS id a2si18178707wiv.49.2014.09.09.07.15.30 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 09 Sep 2014 07:15:31 -0700 (PDT) Received-SPF: pass (google.com: domain of daniel.thompson@linaro.org designates 74.125.82.174 as permitted sender) client-ip=74.125.82.174; Received: by mail-we0-f174.google.com with SMTP id t60so2693084wes.5 for ; Tue, 09 Sep 2014 07:15:30 -0700 (PDT) X-Received: by 10.180.13.195 with SMTP id j3mr12078995wic.70.1410272130759; Tue, 09 Sep 2014 07:15:30 -0700 (PDT) Received: from sundance.lan (cpc4-aztw19-0-0-cust157.18-1.cable.virginm.net. [82.33.25.158]) by mx.google.com with ESMTPSA id k10sm15059594wjb.28.2014.09.09.07.15.29 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 09 Sep 2014 07:15:29 -0700 (PDT) From: Daniel Thompson To: Russell King Cc: Daniel Thompson , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, patches@linaro.org, linaro-kernel@lists.linaro.org, John Stultz , Thomas Gleixner , Sumit Semwal , Jason Cooper Subject: [PATCH v4 4/6] irqchip: gic: Add support for IPI FIQ Date: Tue, 9 Sep 2014 15:15:09 +0100 Message-Id: <1410272111-30516-5-git-send-email-daniel.thompson@linaro.org> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1410272111-30516-1-git-send-email-daniel.thompson@linaro.org> References: <1410190115-32604-1-git-send-email-daniel.thompson@linaro.org> <1410272111-30516-1-git-send-email-daniel.thompson@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: daniel.thompson@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.178 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch provides support for arm's newly added IPI FIQ. It works by placing all interrupt sources *except* IPI FIQ in group 1 and then flips a configuration bit in the GIC such that group 1 interrupts use IRQ and group 0 interrupts use FIQ. All GIC hardware except GICv1-without-TrustZone support provides a means to group exceptions into group 0 and group 1. However the hardware functionality is unavailable to the kernel when a secure monitor is present because access to the grouping registers are prohibited outside "secure world" (a feature that allows grouping to be used to allow hardware peripherals to send interrupts into the secure world). However when grouping is not available we can rely on the GIC's RAZ/WI semantics and avoid conditional code. Tested on Freescale i.MX6 (quad A9, GICv1-with-TrustZone running in secure mode). Signed-off-by: Daniel Thompson Cc: Russell King Cc: Thomas Gleixner Cc: Jason Cooper --- arch/arm/kernel/traps.c | 5 +- drivers/irqchip/irq-gic.c | 140 ++++++++++++++++++++++++++++++++++++++-- include/linux/irqchip/arm-gic.h | 3 + 3 files changed, 142 insertions(+), 6 deletions(-) diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 439138d..92c4ea1 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -479,7 +480,9 @@ asmlinkage void __exception_irq_entry handle_fiq_as_nmi(struct pt_regs *regs) nmi_enter(); - /* nop. FIQ handlers for special arch/arm features can be added here. */ +#ifdef CONFIG_ARM_GIC + gic_handle_fiq_ipi(); +#endif nmi_exit(); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4b959e6..0d67b0e 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -39,8 +39,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -48,6 +50,10 @@ #include "irq-gic-common.h" #include "irqchip.h" +#ifndef SMP_IPI_FIQ_MASK +#define SMP_IPI_FIQ_MASK 0 +#endif + union gic_base { void __iomem *common_base; void __percpu * __iomem *percpu_base; @@ -325,6 +331,94 @@ static struct irq_chip gic_chip = { .irq_set_wake = gic_set_wake, }; +/* + * Shift an interrupt between Group 0 and Group 1. + * + * In addition to changing the group we also modify the priority to + * match what "ARM strongly recommends" for a system where no Group 1 + * interrupt must ever preempt a Group 0 interrupt. + * + * If is safe to call this function on systems which do not support + * grouping (it will have no effect). + */ +static void gic_set_group_irq(void __iomem *base, unsigned int hwirq, + int group) +{ + unsigned int grp_reg = hwirq / 32 * 4; + u32 grp_mask = BIT(hwirq % 32); + u32 grp_val; + + unsigned int pri_reg = (hwirq / 4) * 4; + u32 pri_mask = BIT(7 + ((hwirq % 4) * 8)); + u32 pri_val; + + /* + * Systems which do not support grouping will have no bits + * set in IGROUP[0] (and all systems which do will have set bits). + */ + grp_val = readl_relaxed(base + GIC_DIST_IGROUP + 0); + if (!grp_val) + return; + + raw_spin_lock(&irq_controller_lock); + + grp_val = readl_relaxed(base + GIC_DIST_IGROUP + grp_reg); + pri_val = readl_relaxed(base + GIC_DIST_PRI + pri_reg); + + if (group) { + grp_val |= grp_mask; + pri_val |= pri_mask; + } else { + grp_val &= ~grp_mask; + pri_val &= ~pri_mask; + } + + writel_relaxed(grp_val, base + GIC_DIST_IGROUP + grp_reg); + writel_relaxed(pri_val, base + GIC_DIST_PRI + pri_reg); + + raw_spin_unlock(&irq_controller_lock); +} + +/* + * Test which group an interrupt belongs to. + * + * Returns 0 if the controller does not support grouping. + */ +static int gic_get_group_irq(void __iomem *base, unsigned int hwirq) +{ + unsigned int grp_reg = hwirq / 32 * 4; + u32 grp_val; + + grp_val = readl_relaxed(base + GIC_DIST_IGROUP + grp_reg); + + return (grp_val >> (hwirq % 32)) & 1; +} + +/* + * Fully acknowledge (both ack and eoi) any outstanding FIQ-based IPI, + * otherwise do nothing. + */ +void gic_handle_fiq_ipi(void) +{ + struct gic_chip_data *gic = &gic_data[0]; + void __iomem *cpu_base = gic_data_cpu_base(gic); + unsigned long irqstat, irqnr; + + if (WARN_ON(!in_nmi())) + return; + + while ((1u << readl_relaxed(cpu_base + GIC_CPU_HIGHPRI)) & + SMP_IPI_FIQ_MASK) { + irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); + writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); + + irqnr = irqstat & GICC_IAR_INT_ID_MASK; + WARN_RATELIMIT(irqnr > 16, + "Unexpected irqnr %lu (bad prioritization?)\n", + irqnr); + } +} + void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) { if (gic_nr >= MAX_GIC_NR) @@ -373,7 +467,18 @@ static void __init gic_dist_init(struct gic_chip_data *gic) gic_dist_config(base, gic_irqs, NULL); - writel_relaxed(1, base + GIC_DIST_CTRL); + /* + * Set all global interrupts to be group 1 (this register is + * RAZ/WI if not accessible in current mode) + */ + for (i = 32; i < gic_irqs; i += 32) + writel_relaxed(0xffffffff, base + GIC_DIST_IGROUP + i * 4 / 32); + + /* + * Set EnableGrp1/EnableGrp0 (bit 1 and 0) or EnableGrp (bit 0 only, + * bit 1 ignored) + */ + writel_relaxed(3, base + GIC_DIST_CTRL); } static void gic_cpu_init(struct gic_chip_data *gic) @@ -382,6 +487,7 @@ static void gic_cpu_init(struct gic_chip_data *gic) void __iomem *base = gic_data_cpu_base(gic); unsigned int cpu_mask, cpu = smp_processor_id(); int i; + unsigned long secure_irqs, secure_irq; /* * Get what the GIC says our CPU mask is. @@ -400,8 +506,27 @@ static void gic_cpu_init(struct gic_chip_data *gic) gic_cpu_config(dist_base, NULL); + /* + * Set any PPI and SGI interrupts not set in SMP_IPI_FIQ_MASK + * to be group 1 (this register is RAZ/WI if not accessible) + */ + writel_relaxed(~SMP_IPI_FIQ_MASK, dist_base + GIC_DIST_IGROUP + 0); + + /* + * Update the priority of any resulting group0 interrupts. + */ + secure_irqs = ~readl_relaxed(dist_base + GIC_DIST_IGROUP + 0); + for_each_set_bit(secure_irq, &secure_irqs, 16) + gic_set_group_irq(dist_base, i, 0); + writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); - writel_relaxed(1, base + GIC_CPU_CTRL); + + /* The bottom most bit will be set for all GIC variants (and is + * called Enable or EnableGrp0 depending on operating mode). The + * remaining four bits (CBPR, FIQEn, AckCtl and EnableGrp1) are + * RAZ/WI if not accessible. + */ + writel_relaxed(0x1f, base + GIC_CPU_CTRL); } void gic_cpu_if_down(void) @@ -485,7 +610,7 @@ static void gic_dist_restore(unsigned int gic_nr) writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); - writel_relaxed(1, dist_base + GIC_DIST_CTRL); + writel_relaxed(3, dist_base + GIC_DIST_CTRL); } static void gic_cpu_save(unsigned int gic_nr) @@ -542,7 +667,7 @@ static void gic_cpu_restore(unsigned int gic_nr) writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4); writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); - writel_relaxed(1, cpu_base + GIC_CPU_CTRL); + writel_relaxed(0x1f, cpu_base + GIC_CPU_CTRL); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) @@ -604,6 +729,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { int cpu; unsigned long flags, map = 0; + unsigned long softint; raw_spin_lock_irqsave(&irq_controller_lock, flags); @@ -618,7 +744,11 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) dmb(ishst); /* this always happens on GIC0 */ - writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); + softint = map << 16 | irq; + if (gic_get_group_irq(gic_data_dist_base(&gic_data[0]), irq)) + softint |= 0x8000; + writel_relaxed(softint, + gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 45e2d8c..52a5676 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -101,5 +101,8 @@ static inline void __init register_routable_domain_ops { gic_routable_irq_domain_ops = ops; } + +void gic_handle_fiq_ipi(void); + #endif /* __ASSEMBLY */ #endif