aboutsummaryrefslogtreecommitdiff
path: root/drivers/irqchip/irq-gic-v3.c
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2015-03-17 16:29:22 +0000
committerDaniel Thompson <daniel.thompson@linaro.org>2015-03-18 14:18:00 +0000
commite0e6cdc3e77ae3af0758387c6ad674961f99cb94 (patch)
treef9e0a3674c1820a0317dc693684e6161ef8f4717 /drivers/irqchip/irq-gic-v3.c
parent0c10fa0278aebd0552d14cedbb5a205710aaf8ce (diff)
downloadlinux-e0e6cdc3e77ae3af0758387c6ad674961f99cb94.tar.gz
arm64: irqflags: Use ICC sysregs to implement IRQ masking
Currently irqflags is implemented using the PSR's I bit. It is possible to implement irqflags by using the co-processor interface to the GIC. Using the co-processor interface makes it feasible to simulate NMIs using GIC interrupt prioritization. This patch changes the irqflags macros to modify, save and restore ICC_PMR_EL1. This has a substantial knock on effect for the rest of the kernel. There are three reasons for this: 1. The state of the ICC_PMR_EL1_G_BIT becomes part of the CPU context and must be saved and restored during traps. To simplify the additional context management the ICC_PMR_EL1_G_BIT is converted into a fake (reserved) bit within the PSR (PSR_G_BIT). Naturally this approach will need to be changed if future ARM architecture extensions make use of this bit. 2. The hardware automatically masks the I bit (at boot, during traps, etc). When the I bit is set by hardware we must add code to switch from I bit masking and PMR masking. 3. Some instructions, noteably wfi, require that the PMR not be used for interrupt masking. Before calling these instructions we must switch from PMR masking to I bit masking. Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Diffstat (limited to 'drivers/irqchip/irq-gic-v3.c')
-rw-r--r--drivers/irqchip/irq-gic-v3.c29
1 files changed, 28 insertions, 1 deletions
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 32533650494c..3923b2a2150c 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -110,8 +110,33 @@ static void gic_redist_wait_for_rwp(void)
static u64 __maybe_unused gic_read_iar(void)
{
u64 irqstat;
+ u64 __maybe_unused daif;
+ u64 __maybe_unused pmr;
+ u64 __maybe_unused default_pmr_value = DEFAULT_PMR_VALUE;
+#ifndef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+#else
+ /*
+ * The PMR may be configured to mask interrupts when this code is
+ * called, thus in order to acknowledge interrupts we must set the
+ * PMR to its default value before reading from the IAR.
+ *
+ * To do this without taking an interrupt we also ensure the I bit
+ * is set whilst we are interfering with the value of the PMR.
+ */
+ asm volatile(
+ "mrs %1, daif\n" /* save I bit */
+ "msr daifset, #2\n" /* set I bit */
+ "mrs_s %2, " __stringify(ICC_PMR_EL1) "\n" /* save PMR */
+ "msr_s " __stringify(ICC_PMR_EL1) ",%3\n" /* set PMR */
+ "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n" /* ack int */
+ "msr_s " __stringify(ICC_PMR_EL1) ",%2\n" /* restore PMR */
+ "isb\n"
+ "msr daif, %1" /* restore I */
+ : "=r" (irqstat), "=&r" (daif), "=&r" (pmr)
+ : "r" (default_pmr_value));
+#endif
return irqstat;
}
@@ -142,7 +167,7 @@ static void __maybe_unused gic_write_sgi1r(u64 val)
asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
}
-static void gic_enable_sre(void)
+static void __maybe_unused gic_enable_sre(void)
{
u64 val;
@@ -382,11 +407,13 @@ static int gic_populate_rdist(void)
static void gic_cpu_sys_reg_init(void)
{
+#ifndef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
/* Enable system registers */
gic_enable_sre();
/* Set priority mask register */
gic_write_pmr(DEFAULT_PMR_VALUE);
+#endif
/*
* On FVP, CPU 0 arrives in the kernel with its BPR changed from the