]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: GICv3 initialisation
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Fri, 11 Jul 2014 15:33:28 +0000 (16:33 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:07 +0000 (11:04 +0100)
Assuming there is a GIC distributor at address GICD_BASE, this patch
checks its version and call the gic init function. Linux's kconfig header
is used to guess the base address of the distributor.
Ideally, a device tree would be passed to the hypervisor in the root
cell's config, allowing to remove all constant base addresses.

The patch also assumes that most of the GIC has been setup by Linux prior
to the hypervisor installation, and only initialises the vGIC.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Jan: use mmio accessors]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/Makefile
hypervisor/arch/arm/gic-v3.c [new file with mode: 0644]
hypervisor/arch/arm/include/asm/gic_common.h [new file with mode: 0644]
hypervisor/arch/arm/include/asm/gic_v3.h [new file with mode: 0644]
hypervisor/arch/arm/include/asm/percpu.h
hypervisor/arch/arm/include/asm/platform.h
hypervisor/arch/arm/irqchip.c

index 1383fbde8c01aa199093916eb25d474ea2dc7168..e3a8b53389272635482ad3420ad44f1fe27b82df 100644 (file)
@@ -19,4 +19,5 @@ always := built-in.o
 obj-y := entry.o dbg-write.o exception.o setup.o lib.o traps.o
 obj-y += paging.o mmu_hyp.o mmu_cell.o
 obj-y += irqchip.o
+obj-$(CONFIG_ARM_GIC_V3) += gic-v3.o
 obj-$(CONFIG_SERIAL_AMBA_PL011) += dbg-write-pl011.o
diff --git a/hypervisor/arch/arm/gic-v3.c b/hypervisor/arch/arm/gic-v3.c
new file mode 100644 (file)
index 0000000..2c3e424
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/mmio.h>
+#include <jailhouse/printk.h>
+#include <jailhouse/processor.h>
+#include <jailhouse/types.h>
+#include <asm/gic_common.h>
+#include <asm/irqchip.h>
+#include <asm/platform.h>
+#include <asm/setup.h>
+
+/*
+ * This implementation assumes that the kernel driver already initialised most
+ * of the GIC.
+ * There is almost no instruction barrier, since IRQs are always disabled in the
+ * hyp, and ERET serves as the context synchronization event.
+ */
+
+static unsigned int gic_num_lr;
+
+static void *gicr_base;
+static unsigned int gicr_size;
+
+static int gic_init(void)
+{
+       int err;
+
+       /* FIXME: parse a dt */
+       gicr_base = GICR_BASE;
+       gicr_size = GICR_SIZE;
+
+       /* Let the per-cpu code access the redistributors */
+       err = arch_map_device(gicr_base, gicr_base, gicr_size);
+
+       return err;
+}
+
+static int gic_cpu_init(struct per_cpu *cpu_data)
+{
+       u64 typer;
+       u32 pidr;
+       u32 gic_version;
+       u32 cell_icc_ctlr, cell_icc_pmr, cell_icc_igrpen1;
+       u32 ich_vtr;
+       u32 ich_vmcr;
+       void *redist_base = gicr_base;
+
+       /* Find redistributor */
+       do {
+               pidr = mmio_read32(redist_base + GICR_PIDR2);
+               gic_version = GICR_PIDR2_ARCH(pidr);
+               if (gic_version != 3 && gic_version != 4)
+                       break;
+
+               typer = mmio_read64(redist_base + GICR_TYPER);
+               if ((typer >> 32) == cpu_data->cpu_id) {
+                       cpu_data->gicr_base = redist_base;
+                       break;
+               }
+
+               redist_base += 0x20000;
+               if (gic_version == 4)
+                       redist_base += 0x20000;
+       } while (!(typer & GICR_TYPER_Last));
+
+       if (cpu_data->gicr_base == 0) {
+               printk("GIC: No redist found for CPU%d\n", cpu_data->cpu_id);
+               return -ENODEV;
+       }
+
+       /* Ensure all IPIs are enabled */
+       mmio_write32(redist_base + GICR_SGI_BASE + GICR_ISENABLER, 0x0000ffff);
+
+       /*
+        * Set EOIMode to 1
+        * This allow to drop the priority of level-triggered interrupts without
+        * deactivating them, and thus ensure that they won't be immediately
+        * re-triggered. (e.g. timer)
+        * They can then be injected into the guest using the LR.HW bit, and
+        * will be deactivated once the guest does an EOI after handling the
+        * interrupt source.
+        */
+       arm_read_sysreg(ICC_CTLR_EL1, cell_icc_ctlr);
+       arm_write_sysreg(ICC_CTLR_EL1, ICC_CTLR_EOImode);
+
+       arm_read_sysreg(ICC_PMR_EL1, cell_icc_pmr);
+       arm_write_sysreg(ICC_PMR_EL1, ICC_PMR_DEFAULT);
+
+       arm_read_sysreg(ICC_IGRPEN1_EL1, cell_icc_igrpen1);
+       arm_write_sysreg(ICC_IGRPEN1_EL1, ICC_IGRPEN1_EN);
+
+       arm_read_sysreg(ICH_VTR_EL2, ich_vtr);
+       gic_num_lr = (ich_vtr & 0xf) + 1;
+
+       ich_vmcr = (cell_icc_pmr & ICC_PMR_MASK) << ICH_VMCR_VPMR_SHIFT;
+       if (cell_icc_igrpen1 & ICC_IGRPEN1_EN)
+               ich_vmcr |= ICH_VMCR_VENG1;
+       if (cell_icc_ctlr & ICC_CTLR_EOImode)
+               ich_vmcr |= ICH_VMCR_VEOIM;
+       arm_write_sysreg(ICH_VMCR_EL2, ich_vmcr);
+
+       /* After this, the cells access the virtual interface of the GIC. */
+       arm_write_sysreg(ICH_HCR_EL2, ICH_HCR_EN);
+
+       return 0;
+}
+
+static int gic_send_sgi(struct sgi *sgi)
+{
+       u64 val;
+       u16 targets = sgi->targets;
+
+       if (!is_sgi(sgi->id))
+               return -EINVAL;
+
+       if (sgi->routing_mode == 2)
+               targets = 1 << phys_processor_id();
+
+       val = (u64)sgi->aff3 << ICC_SGIR_AFF3_SHIFT
+           | (u64)sgi->aff2 << ICC_SGIR_AFF2_SHIFT
+           | sgi->aff1 << ICC_SGIR_AFF1_SHIFT
+           | (targets & ICC_SGIR_TARGET_MASK)
+           | (sgi->id & 0xf) << ICC_SGIR_IRQN_SHIFT;
+
+       if (sgi->routing_mode == 1)
+               val |= ICC_SGIR_ROUTING_BIT;
+
+       /*
+        * Ensure the targets see our modifications to their per-cpu
+        * structures.
+        */
+       dsb(ish);
+
+       arm_write_sysreg(ICC_SGI1R_EL1, val);
+       isb();
+
+       return 0;
+}
+
+static void gic_handle_irq(struct per_cpu *cpu_data)
+{
+}
+
+struct irqchip_ops gic_irqchip = {
+       .init = gic_init,
+       .cpu_init = gic_cpu_init,
+       .send_sgi = gic_send_sgi,
+       .handle_irq = gic_handle_irq,
+};
diff --git a/hypervisor/arch/arm/include/asm/gic_common.h b/hypervisor/arch/arm/include/asm/gic_common.h
new file mode 100644 (file)
index 0000000..a86109c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _JAILHOUSE_ASM_GIC_COMMON_H
+#define _JAILHOUSE_ASM_GIC_COMMON_H
+
+#include <jailhouse/types.h>
+
+#define GICD_CTLR                      0x0000
+#define GICD_TYPER                     0x0004
+#define GICD_IIDR                      0x0008
+#define GICD_IGROUPR                   0x0080
+#define GICD_ISENABLER                 0x0100
+#define GICD_ICENABLER                 0x0180
+#define GICD_ISPENDR                   0x0200
+#define GICD_ICPENDR                   0x0280
+#define GICD_ISACTIVER                 0x0300
+#define GICD_ICACTIVER                 0x0380
+#define GICD_IPRIORITYR                        0x0400
+#define GICD_ITARGETSR                 0x0800
+#define GICD_ICFGR                     0x0c00
+#define GICD_NSACR                     0x0e00
+#define GICD_SGIR                      0x0f00
+#define GICD_CPENDSGIR                 0x0f10
+#define GICD_SPENDSGIR                 0x0f20
+#define GICD_IROUTER                   0x6000
+
+#define GICD_PIDR2_ARCH(pidr)          (((pidr) & 0xf0) >> 4)
+
+#define is_sgi(irqn)                   ((u32)(irqn) < 16)
+#define is_ppi(irqn)                   ((irqn) > 15 && (irqn) < 32)
+#define is_spi(irqn)                   ((irqn) > 31 && (irqn) < 1020)
+
+#endif /* !_JAILHOUSE_ASM_GIC_COMMON_H */
diff --git a/hypervisor/arch/arm/include/asm/gic_v3.h b/hypervisor/arch/arm/include/asm/gic_v3.h
new file mode 100644 (file)
index 0000000..6e2ddb6
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _JAILHOUSE_ASM_GIC_V3_H
+#define _JAILHOUSE_ASM_GIC_V3_H
+
+#include <asm/sysregs.h>
+
+#define GICD_CIDR0             0xfff0
+#define GICD_CIDR1             0xfff4
+#define GICD_CIDR2             0xfff8
+#define GICD_CIDR3             0xfffc
+
+#define GICD_PIDR0             0xffe0
+#define GICD_PIDR1             0xffe4
+#define GICD_PIDR2             0xffe8
+#define GICD_PIDR3             0xffec
+#define GICD_PIDR4             0xffd0
+#define GICD_PIDR5             0xffd4
+#define GICD_PIDR6             0xffd8
+#define GICD_PIDR7             0xffdc
+
+#define GICR_CTLR              GICD_CTLR
+#define GICR_TYPER             0x0008
+#define GICR_WAKER             0x0014
+#define GICR_CIDR0             GICD_CIDR0
+#define GICR_CIDR1             GICD_CIDR1
+#define GICR_CIDR2             GICD_CIDR2
+#define GICR_CIDR3             GICD_CIDR3
+#define GICR_PIDR0             GICD_PIDR0
+#define GICR_PIDR1             GICD_PIDR1
+#define GICR_PIDR2             GICD_PIDR2
+#define GICR_PIDR3             GICD_PIDR3
+#define GICR_PIDR4             GICD_PIDR4
+#define GICR_PIDR5             GICD_PIDR5
+#define GICR_PIDR6             GICD_PIDR6
+#define GICR_PIDR7             GICD_PIDR7
+
+#define GICR_SGI_BASE          0x10000
+#define GICR_IGROUPR           GICD_IGROUPR
+#define GICR_ISENABLER         GICD_ISENABLER
+#define GICR_ICENABLER         GICD_ICENABLER
+#define GICR_ISACTIVER         GICD_ISACTIVER
+#define GICR_ICACTIVER         GICD_ICACTIVER
+#define GICR_IPRIORITY         GICD_IPRIORITY
+
+#define GICR_TYPER_Last                (1 << 4)
+#define GICR_PIDR2_ARCH                GICD_PIDR2_ARCH
+
+#define ICC_IAR1_EL1           SYSREG_32(0, c12, c12, 0)
+#define ICC_EOIR1_EL1          SYSREG_32(0, c12, c12, 1)
+#define ICC_HPPIR1_EL1         SYSREG_32(0, c12, c12, 2)
+#define ICC_BPR1_EL1           SYSREG_32(0, c12, c12, 3)
+#define ICC_DIR_EL1            SYSREG_32(0, c12, c11, 1)
+#define ICC_PMR_EL1            SYSREG_32(0, c4, c6, 0)
+#define ICC_RPR_EL1            SYSREG_32(0, c12, c11, 3)
+#define ICC_CTLR_EL1           SYSREG_32(0, c12, c12, 4)
+#define ICC_SRE_EL1            SYSREG_32(0, c12, c12, 5)
+#define ICC_SRE_EL2            SYSREG_32(4, c12, c9, 5)
+#define ICC_IGRPEN1_EL1                SYSREG_32(0, c12, c12, 7)
+#define ICC_SGI1R_EL1          SYSREG_64(0, c12)
+
+#define ICH_HCR_EL2            SYSREG_32(4, c12, c11, 0)
+#define ICH_VTR_EL2            SYSREG_32(4, c12, c11, 1)
+#define ICH_MISR_EL2           SYSREG_32(4, c12, c11, 2)
+#define ICH_EISR_EL2           SYSREG_32(4, c12, c11, 3)
+#define ICH_ELSR_EL2           SYSREG_32(4, c12, c11, 5)
+#define ICH_VMCR_EL2           SYSREG_32(4, c12, c11, 7)
+
+/* Different on AArch32 and AArch64... */
+#define __ICH_LR0(x)           SYSREG_32(4, c12, c12, x)
+#define __ICH_LR8(x)           SYSREG_32(4, c12, c13, x)
+#define __ICH_LRC0(x)          SYSREG_32(4, c12, c14, x)
+#define __ICH_LRC8(x)          SYSREG_32(4, c12, c15, x)
+
+#define ICH_LR0                        __ICH_LR0(0)
+#define ICH_LR1                        __ICH_LR0(1)
+#define ICH_LR2                        __ICH_LR0(2)
+#define ICH_LR3                        __ICH_LR0(3)
+#define ICH_LR4                        __ICH_LR0(4)
+#define ICH_LR5                        __ICH_LR0(5)
+#define ICH_LR6                        __ICH_LR0(6)
+#define ICH_LR7                        __ICH_LR0(7)
+#define ICH_LR8                        __ICH_LR8(0)
+#define ICH_LR9                        __ICH_LR8(1)
+#define ICH_LR10               __ICH_LR8(2)
+#define ICH_LR11               __ICH_LR8(3)
+#define ICH_LR12               __ICH_LR8(4)
+#define ICH_LR13               __ICH_LR8(5)
+#define ICH_LR14               __ICH_LR8(6)
+#define ICH_LR15               __ICH_LR8(7)
+#define ICH_LRC0               __ICH_LRC0(0)
+#define ICH_LRC1               __ICH_LRC0(1)
+#define ICH_LRC2               __ICH_LRC0(2)
+#define ICH_LRC3               __ICH_LRC0(3)
+#define ICH_LRC4               __ICH_LRC0(4)
+#define ICH_LRC5               __ICH_LRC0(5)
+#define ICH_LRC6               __ICH_LRC0(6)
+#define ICH_LRC7               __ICH_LRC0(7)
+#define ICH_LRC8               __ICH_LRC8(0)
+#define ICH_LRC9               __ICH_LRC8(1)
+#define ICH_LRC10              __ICH_LRC8(2)
+#define ICH_LRC11              __ICH_LRC8(3)
+#define ICH_LRC12              __ICH_LRC8(4)
+#define ICH_LRC13              __ICH_LRC8(5)
+#define ICH_LRC14              __ICH_LRC8(6)
+#define ICH_LRC15              __ICH_LRC8(7)
+
+#define ICC_CTLR_EOImode       0x2
+#define ICC_PMR_MASK           0xff
+#define ICC_PMR_DEFAULT                0xf0
+#define ICC_IGRPEN1_EN         0x1
+
+#define ICC_SGIR_AFF3_SHIFT    48
+#define ICC_SGIR_AFF2_SHIFT    32
+#define ICC_SGIR_AFF1_SHIFT    16
+#define ICC_SGIR_TARGET_MASK   0xffff
+#define ICC_SGIR_IRQN_SHIFT    24
+#define ICC_SGIR_ROUTING_BIT   (1ULL << 40)
+
+#define ICH_HCR_EN             (1 << 0)
+#define ICH_HCR_UIE            (1 << 1)
+#define ICH_HCR_LRENPIE                (1 << 2)
+#define ICH_HCR_NPIE           (1 << 3)
+#define ICH_HCR_VGRP0EIE       (1 << 4)
+#define ICH_HCR_VGRP0DIE       (1 << 5)
+#define ICH_HCR_VGRP1EIE       (1 << 6)
+#define ICH_HCR_VGRP1DIE       (1 << 7)
+#define ICH_HCR_VARE           (1 << 9)
+#define ICH_HCR_TC             (1 << 10)
+#define ICH_HCR_TALL0          (1 << 11)
+#define ICH_HCR_TALL1          (1 << 12)
+#define ICH_HCR_TSEI           (1 << 13)
+#define ICH_HCR_EOICount       (0x1f << 27)
+
+#define ICH_MISR_EOI           (1 << 0)
+#define ICH_MISR_U             (1 << 1)
+#define ICH_MISR_LRENP         (1 << 2)
+#define ICH_MISR_NP            (1 << 3)
+#define ICH_MISR_VGRP0E                (1 << 4)
+#define ICH_MISR_VGRP0D                (1 << 5)
+#define ICH_MISR_VGRP1E                (1 << 6)
+#define ICH_MISR_VGRP1D                (1 << 7)
+
+#define ICH_VMCR_VENG0         (1 << 0)
+#define ICH_VMCR_VENG1         (1 << 1)
+#define ICH_VMCR_VACKCTL       (1 << 2)
+#define ICH_VMCR_VFIQEN                (1 << 3)
+#define ICH_VMCR_VCBPR         (1 << 4)
+#define ICH_VMCR_VEOIM         (1 << 9)
+#define ICH_VMCR_VBPR1_SHIFT   18
+#define ICH_VMCR_VBPR0_SHIFT   21
+#define ICH_VMCR_VPMR_SHIFT    24
+
+/* List registers upper bits */
+#define ICH_LR_INVALID         (0x0ULL << 62)
+#define ICH_LR_PENDING         (0x1ULL << 62)
+#define ICH_LR_ACTIVE          (0x2ULL << 62)
+#define ICH_LR_PENDACTIVE      (0x3ULL << 62)
+#define ICH_LR_HW_BIT          (0x1ULL << 61)
+#define ICH_LR_GROUP_BIT       (0x1ULL << 60)
+#define ICH_LR_PRIORITY_SHIFT  48
+#define ICH_LR_SGI_EOI         (0x1ULL << 41)
+#define ICH_LR_PHYS_ID_SHIFT   32
+
+#ifndef __ASSEMBLY__
+
+#include <jailhouse/types.h>
+
+static inline u64 gic_read_lr(unsigned int n)
+{
+       u32 lr, lrc;
+
+       switch (n) {
+#define __READ_LR(n)                                   \
+       case n:                                         \
+               arm_read_sysreg(ICH_LR##n, lr);         \
+               arm_read_sysreg(ICH_LRC##n, lrc);       \
+               break;
+
+       __READ_LR(0)
+       __READ_LR(1)
+       __READ_LR(2)
+       __READ_LR(3)
+       __READ_LR(4)
+       __READ_LR(5)
+       __READ_LR(6)
+       __READ_LR(7)
+       __READ_LR(8)
+       __READ_LR(9)
+       __READ_LR(10)
+       __READ_LR(11)
+       __READ_LR(12)
+       __READ_LR(13)
+       __READ_LR(14)
+       __READ_LR(15)
+#undef __READ_LR
+
+       default:
+               return (u64)(-1);
+       }
+
+       return (u64)lrc << 32 | lr;
+}
+
+static inline void gic_write_lr(unsigned int n, u64 val)
+{
+       u32 lr = (u32)val;
+       u32 lrc = val >> 32;
+
+       switch (n) {
+#define __WRITE_LR(n)                                  \
+       case n:                                         \
+               arm_write_sysreg(ICH_LR##n, lr);        \
+               arm_write_sysreg(ICH_LRC##n, lrc);      \
+               break;
+
+       __WRITE_LR(0)
+       __WRITE_LR(1)
+       __WRITE_LR(2)
+       __WRITE_LR(3)
+       __WRITE_LR(4)
+       __WRITE_LR(5)
+       __WRITE_LR(6)
+       __WRITE_LR(7)
+       __WRITE_LR(8)
+       __WRITE_LR(9)
+       __WRITE_LR(10)
+       __WRITE_LR(11)
+       __WRITE_LR(12)
+       __WRITE_LR(13)
+       __WRITE_LR(14)
+       __WRITE_LR(15)
+#undef __WRITE_LR
+       }
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* _JAILHOUSE_ASM_GIC_V3_H */
index 0c764528cbb119236674bad84d03203a60ab67ad..bc3db93fff8fc2155e6a4b155fa92df0cf39cf54 100644 (file)
@@ -37,7 +37,9 @@ struct per_cpu {
        unsigned long linux_reg[NUM_ENTRY_REGS];
 
        unsigned int cpu_id;
-//     u32 apic_id;
+       /* Only GICv3: redistributor base */
+       void *gicr_base;
+
        struct cell *cell;
 
        u32 stats[JAILHOUSE_NUM_CPU_STATS];
index 03515b4f71eeaa4f3d11a2fb13f8e0e695a94230..ad00e538abb1ae52d35009935936ca7fcd66eca8 100644 (file)
 #ifndef _JAILHOUSE_ASM_PLATFORM_H
 #define _JAILHOUSE_ASM_PLATFORM_H
 
+/*
+ * All those things are defined in the device tree. This header *must*
+ * disappear. The GIC includes will need to be sanitized in order to avoid ID
+ * naming conflicts.
+ */
 #ifndef __ASSEMBLY__
 
 #ifdef CONFIG_ARCH_VEXPRESS
 
-#define UART_BASE_PHYS ((void *)0x1c090000)
-#define UART_BASE_VIRT ((void *)0xf8090000)
+# define UART_BASE_PHYS        ((void *)0x1c090000)
+# define UART_BASE_VIRT        ((void *)0xf8090000)
+
+# ifdef CONFIG_ARM_GIC_V3
+#  define GICD_BASE    ((void *)0x2f000000)
+#  define GICD_SIZE    0x10000
+#  define GICR_BASE    ((void *)0x2f100000)
+#  define GICR_SIZE    0x100000
+
+#  include <asm/gic_v3.h>
+# endif /* GIC */
 
 #endif /* CONFIG_ARCH_VEXPRESS */
 #endif /* !__ASSEMBLY__ */
index 67eef60af481061d58be70e3c33586973b34991c..0848780eab2baa7e70be74d06cbab65c28206335 100644 (file)
  * the COPYING file in the top-level directory.
  */
 
-#include <asm/irqchip.h>
-#include <asm/sysregs.h>
 #include <jailhouse/entry.h>
+#include <jailhouse/mmio.h>
 #include <jailhouse/paging.h>
 #include <jailhouse/printk.h>
 #include <jailhouse/string.h>
+#include <asm/gic_common.h>
+#include <asm/irqchip.h>
+#include <asm/platform.h>
+#include <asm/setup.h>
+#include <asm/sysregs.h>
+
+/* AMBA's biosfood */
+#define AMBA_DEVICE    0xb105f00d
+
+void *gicd_base;
+unsigned long gicd_size;
 
 /*
  * The init function must be called after the MMU setup, and whilst in the
@@ -42,14 +52,54 @@ int irqchip_cpu_init(struct per_cpu *cpu_data)
        return 0;
 }
 
+/* Only the GIC is implemented */
+extern struct irqchip_ops gic_irqchip;
+
 int irqchip_init(void)
 {
+       int i, err;
+       u32 pidr2, cidr;
+       u32 dev_id = 0;
+
        /* Only executed on master CPU */
        if (irqchip_is_init)
                return 0;
 
-       memset(&irqchip, 0, sizeof(irqchip));
-       irqchip_is_init = true;
+       /* FIXME: parse device tree */
+       gicd_base = GICD_BASE;
+       gicd_size = GICD_SIZE;
+
+       if ((err = arch_map_device(gicd_base, gicd_base, gicd_size)) != 0)
+               return err;
+
+       for (i = 3; i >= 0; i--) {
+               cidr = mmio_read32(gicd_base + GICD_CIDR0 + i * 4);
+               dev_id |= cidr << i * 8;
+       }
+       if (dev_id != AMBA_DEVICE)
+               goto err_no_distributor;
+
+       /* Probe the GIC version */
+       pidr2 = mmio_read32(gicd_base + GICD_PIDR2);
+       switch (GICD_PIDR2_ARCH(pidr2)) {
+       case 0x2:
+               break;
+       case 0x3:
+       case 0x4:
+               memcpy(&irqchip, &gic_irqchip, sizeof(struct irqchip_ops));
+               break;
+       }
+
+       if (irqchip.init) {
+               err = irqchip.init();
+               irqchip_is_init = true;
+
+               return err;
+       }
+
+err_no_distributor:
+       printk("GIC: no distributor found\n");
+       arch_unmap_device(gicd_base, gicd_size);
 
        return -ENODEV;
 }