Thread (26 messages) 26 messages, 2 authors, 2014-06-20
STALE4380d

[PATCH v5 02/20] arm64: initial support for GICv3

From: mark.rutland@arm.com (Mark Rutland)
Date: 2014-06-20 10:02:24
Also in: kvm

Hi Marc,

I have some minor comments below.

On Thu, Jun 19, 2014 at 10:19:25AM +0100, Marc Zyngier wrote:
The Generic Interrupt Controller (version 3) offers services that are
similar to GICv2, with a number of additional features:
- Affinity routing based on the CPU MPIDR (ARE)
- System register for the CPU interfaces (SRE)
- Support for more that 8 CPUs
- Locality-specific Peripheral Interrupts (LPIs)
- Interrupt Translation Services (ITS)

This patch adds preliminary support for GICv3 with ARE and SRE,
non-secure mode only. It relies on higher exception levels to grant ARE
and SRE access.

Support for LPI and ITS will be added at a later time.

Cc: Thomas Gleixner <redacted>
Cc: Jason Cooper <redacted>
Reviewed-by: Zi Shen Lim <redacted>
Reviewed-by: Christoffer Dall <redacted>
Reviewed-by: Tirumalesh Chalamarla <redacted>
Reviewed-by: Yun Wu <redacted>
Reviewed-by: Zhen Lei <redacted>
Tested-by: Tirumalesh Chalamarla<redacted>
Tested-by: Radha Mohan Chintakuntla <redacted>
Acked-by: Radha Mohan Chintakuntla <redacted>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Marc Zyngier <redacted>
---
 arch/arm64/Kconfig                 |   1 +
 arch/arm64/kernel/head.S           |  18 +
 arch/arm64/kernel/hyp-stub.S       |   1 +
 drivers/irqchip/Kconfig            |   5 +
 drivers/irqchip/Makefile           |   1 +
 drivers/irqchip/irq-gic-v3.c       | 690 +++++++++++++++++++++++++++++++++++++
 include/linux/irqchip/arm-gic-v3.h | 193 +++++++++++
 7 files changed, 909 insertions(+)
 create mode 100644 drivers/irqchip/irq-gic-v3.c
 create mode 100644 include/linux/irqchip/arm-gic-v3.h
[...]
+#ifdef CONFIG_ARM_GIC_V3
+       /* GICv3 system register access */
+       mrs     x0, id_aa64pfr0_el1
+       ubfx    x0, x0, #24, #4
+       cmp     x0, #1
+       b.ne    3f
+
+       mrs     x0, ICC_SRE_EL2
+       orr     x0, x0, #1                      // Set ICC_SRE_EL2.SRE==1
+       orr     x0, x0, #(1 << 3)               // Set ICC_SRE_EL2.Enable==1
Could we use macros for these constants?

We already seem to have ICC_SRE_EL2_ENABLE in arm-gic-v3.h, so we'd just
need to add ICC_SRE_EL2_SRE.

The ubfx on the id_aa64pfr0_el1 value probably can't be made nicer with
macros, but I guess we can't have everything.

[...]
+#define DEFAULT_PMR_VALUE      0xf0
Is this an arbitrary value, or chosen for a particular reason? Could we
have a comment either way?
+static void gic_do_wait_for_rwp(void __iomem *base)
+{
+       u32 count = 1000000;    /* 1s! */
I would suggest using USEC_PER_SEC, but it looks like to do so you'd
need to pull in all of <linux/time.h>, so I guess that's not worthwhile.
+
+       while (readl_relaxed(base + GICD_CTLR) & GICD_CTLR_RWP) {
+               count--;
+               if (!count) {
+                       pr_err_ratelimited("RWP timeout, gone fishing\n");
+                       return;
+               }
+               cpu_relax();
+               udelay(1);
+       };
+}
[...]
+/*
+ * Routines to acknowledge, disable and enable interrupts
+ */
This comment looks out of sync with the code; gic_read_iar was earlier.

[...]
+static u64 gic_mpidr_to_affinity(u64 mpidr)
+{
+       u64 aff;
+
+       aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+              MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
+              MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8  |
+              MPIDR_AFFINITY_LEVEL(mpidr, 0)) & ~GICD_IROUTER_SPI_MODE_ANY;
Surely GICD_IROUTER_SPI_MODE_ANY (bit 31) can't ever be set by the rest
of the expression above? Or am I being thick?

[...]
+static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
+{
+       u64 irqnr;
+
+       do {
+               irqnr = gic_read_iar();
+
+               if (likely(irqnr > 15 && irqnr < 1020)) {
+                       u64 irq = irq_find_mapping(gic_data.domain, irqnr);
+                       if (likely(irq)) {
+                               handle_IRQ(irq, regs);
+                               continue;
+                       }
+
+                       WARN_ONCE(true, "Unexpected SPI received!\n");
+                       gic_write_eoir(irqnr);
+               }
+               if (irqnr < 16) {
+                       gic_write_eoir(irqnr);
+#ifdef CONFIG_SMP
+                       handle_IPI(irqnr, regs);
+#else
+                       WARN_ONCE(true, "Unexpected SGI received!\n");
+#endif
+                       continue;
+               }
+       } while (irqnr != 0x3ff);
Any chance we could have a GIC_IRQNR_SPURIOUS macro or similar?

[...]
+static void __init gic_dist_init(void)
+{
+       unsigned int i;
+       u64 affinity;
+       void __iomem *base = gic_data.dist_base;
+
+       /* Disable the distributor */
+       writel_relaxed(0, base + GICD_CTLR);
+       gic_dist_wait_for_rwp();
+
+       gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);
+
+       /* Enable distributor with ARE, Group1 */
+       writel_relaxed(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
+                      base + GICD_CTLR);
+
+       /*
+        * Set all global interrupts to the boot CPU only. ARE must be
+        * enabled.
+        */
+       affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
+       for (i = 32; i < gic_data.irq_nr; i++)
+               writeq_relaxed(affinity, base + GICD_IROUTER + i * 8);
+} +
I assume completion of the GICD_IROUTER writes is guaranteed elsewhere?
+static int gic_populate_rdist(void)
+{
+       u64 mpidr = cpu_logical_map(smp_processor_id());
+       u64 typer;
+       u32 aff;
+       int i;
+
+       /*
+        * Convert affinity to a 32bit value that can be matched to
+        * GICR_TYPER bits [63:32].
+        */
+       aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 24 |
+              MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
+              MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
+              MPIDR_AFFINITY_LEVEL(mpidr, 0));
+
+       for (i = 0; i < gic_data.redist_regions; i++) {
+               void __iomem *ptr = gic_data.redist_base[i];
+               u32 reg;
+
+               reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
+               if (reg != 0x30 && reg != 0x40) { /* We're in trouble... */
+                       pr_warn("No redistributor present @%p\n", ptr);
+                       break;
+               }
What are these magic numbers?

[...]
+static int __init gic_of_init(struct device_node *node, struct device_node *parent)
+{
+       void __iomem *dist_base;
+       void __iomem **redist_base;
+       u64 redist_stride;
+       u32 redist_regions;
+       u32 reg;
+       int gic_irqs;
+       int err;
+       int i;
+
+       dist_base = of_iomap(node, 0);
+       if (!dist_base) {
+               pr_err("%s: unable to map gic dist registers\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+       if (reg != 0x30 && reg != 0x40) {
+               pr_err("%s: no distributor detected, giving up\n",
+                       node->full_name);
+               err = -ENODEV;
+               goto out_unmap_dist;
+       }
The magic numbers strike again...

Cheers,
Mark.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help