From 571d3b54dbaa28f75482aaa7cf27ebaac53eeac4 Mon Sep 17 00:00:00 2001 From: Bradley Bolen Date: Wed, 18 Apr 2018 12:13:06 +0300 Subject: [PATCH] interrupt_controller: gic: Add support for the GIC400 The GIC400 is a common interrupt controller that can be used with the Cortex A and R series processors. This patch adds basic interrupt handling for the GIC, but does not handle multiple routing or priorities. Signed-off-by: Bradley Bolen --- drivers/interrupt_controller/CMakeLists.txt | 1 + drivers/interrupt_controller/Kconfig | 7 + drivers/interrupt_controller/gic-400.c | 246 ++++++++++++++++++ .../interrupt-controller/arm,gic.yaml | 29 +++ .../interrupt-controller/arm-gic.h | 23 ++ 5 files changed, 306 insertions(+) create mode 100644 drivers/interrupt_controller/gic-400.c create mode 100644 dts/bindings/interrupt-controller/arm,gic.yaml create mode 100644 include/dt-bindings/interrupt-controller/arm-gic.h diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index e73d0fff2c8..96fab705736 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 zephyr_sources_ifdef(CONFIG_ARCV2_INTERRUPT_UNIT arcv2_irq_unit.c) +zephyr_sources_ifdef(CONFIG_GIC gic-400.c) zephyr_sources_ifdef(CONFIG_IOAPIC ioapic_intr.c) zephyr_sources_ifdef(CONFIG_LOAPIC loapic_intr.c system_apic.c) zephyr_sources_ifdef(CONFIG_LOAPIC_SPURIOUS_VECTOR loapic_spurious.S) diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index 0fc1b16ff3e..b67e53d8bd1 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -147,6 +147,13 @@ config DW_ICTL_INIT_PRIORITY help DesignWare Interrupt Controller initialization priority. +config GIC + bool "ARM Generic Interrupt Controller (GIC)" + depends on CPU_CORTEX_R + help + The ARM Generic Interrupt Controller works with Cortex-A and + Cortex-R processors. + source "drivers/interrupt_controller/Kconfig.stm32" source "drivers/interrupt_controller/Kconfig.multilevel" diff --git a/drivers/interrupt_controller/gic-400.c b/drivers/interrupt_controller/gic-400.c new file mode 100644 index 00000000000..774aedb5118 --- /dev/null +++ b/drivers/interrupt_controller/gic-400.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2018 Marvell + * Copyright (c) 2018 Lexmark International, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define DT_GIC_DIST_BASE DT_INST_0_ARM_GIC_BASE_ADDRESS_0 +#define DT_GIC_CPU_BASE DT_INST_0_ARM_GIC_BASE_ADDRESS_1 + +#define GICD_CTRL (DT_GIC_DIST_BASE + 0) +#define GICD_TYPER (DT_GIC_DIST_BASE + 0x4) +#define GICD_IIDR (DT_GIC_DIST_BASE + 0x8) +#define GICD_IGROUPRn (DT_GIC_DIST_BASE + 0x80) +#define GICD_ISENABLERn (DT_GIC_DIST_BASE + 0x100) +#define GICD_ICENABLERn (DT_GIC_DIST_BASE + 0x180) +#define GICD_ISPENDRn (DT_GIC_DIST_BASE + 0x200) +#define GICD_ICPENDRn (DT_GIC_DIST_BASE + 0x280) +#define GICD_ISACTIVERn (DT_GIC_DIST_BASE + 0x300) +#define GICD_ICACTIVERn (DT_GIC_DIST_BASE + 0x380) +#define GICD_IPRIORITYRn (DT_GIC_DIST_BASE + 0x400) +#define GICD_ITARGETSRn (DT_GIC_DIST_BASE + 0x800) +#define GICD_ICFGRn (DT_GIC_DIST_BASE + 0xc00) +#define GICD_SGIR (DT_GIC_DIST_BASE + 0xf00) + +#define GICC_CTRL (DT_GIC_CPU_BASE + 0x00) +#define GICC_PMR (DT_GIC_CPU_BASE + 0x04) +#define GICC_BPR (DT_GIC_CPU_BASE + 0x08) +#define GICC_IAR (DT_GIC_CPU_BASE + 0x0c) +#define GICC_EOIR (DT_GIC_CPU_BASE + 0x10) + +#define GICC_ENABLE 3 +#define GICC_DIS_BYPASS_MASK 0x1e0 + +#define NO_GIC_INT_PENDING 1023 + +#define GIC_SPI_INT_BASE 32 + +#define GIC_INT_TYPE_MASK 0x3 +#define GIC_INT_TYPE_EDGE (1 << 1) + +struct gic_ictl_config { + u32_t isr_table_offset; +}; + +static void gic_dist_init(void) +{ + unsigned int gic_irqs, i; + + gic_irqs = sys_read32(GICD_TYPER) & 0x1f; + gic_irqs = (gic_irqs + 1) * 32; + if (gic_irqs > 1020) + gic_irqs = 1020; + + /* + * Disable the forwarding of pending interrupts + * from the Distributor to the CPU interfaces + */ + sys_write32(0, GICD_CTRL); + + /* + * Set all global interrupts to this CPU only. + */ + for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 4) + sys_write32(0x01010101, GICD_ITARGETSRn + i); + + /* + * Set all global interrupts to be level triggered, active low. + */ + for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 16) + sys_write32(0, GICD_ICFGRn + i / 4); + + /* Set priority on all global interrupts. */ + for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 4) + sys_write32(0, GICD_IPRIORITYRn + i); + + /* Set all interrupts to group 0 */ + for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 32) + sys_write32(0, GICD_IGROUPRn + i / 8); + + /* + * Disable all interrupts. Leave the PPI and SGIs alone + * as these enables are banked registers. + */ + for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 32) { + sys_write32(0xffffffff, GICD_ICACTIVERn + i / 8); + sys_write32(0xffffffff, GICD_ICENABLERn + i / 8); + } + + /* + * Enable the forwarding of pending interrupts + * from the Distributor to the CPU interfaces + */ + sys_write32(1, GICD_CTRL); +} + +static void gic_cpu_init(void) +{ + int i; + u32_t val; + + /* + * Deal with the banked PPI and SGI interrupts - disable all + * PPI interrupts, ensure all SGI interrupts are enabled. + */ + sys_write32(0xffffffff, GICD_ICACTIVERn); + sys_write32(0xffff0000, GICD_ICENABLERn); + sys_write32(0x0000ffff, GICD_ISENABLERn); + + /* + * Set priority on PPI and SGI interrupts + */ + for (i = 0; i < 32; i += 4) + sys_write32(0xa0a0a0a0, GICD_IPRIORITYRn + i); + + sys_write32(0xf0, GICC_PMR); + + /* + * Enable interrupts and signal them using the IRQ signal. + */ + val = sys_read32(GICC_CTRL); + val &= GICC_DIS_BYPASS_MASK; + val |= GICC_ENABLE; + sys_write32(val, GICC_CTRL); +} + +static void gic_irq_enable(struct device *dev, unsigned int irq) +{ + int int_grp, int_off; + + irq += GIC_SPI_INT_BASE; + int_grp = irq / 32; + int_off = irq % 32; + + sys_write32((1 << int_off), (GICD_ISENABLERn + int_grp * 4)); +} + +static void gic_irq_disable(struct device *dev, unsigned int irq) +{ + int int_grp, int_off; + + irq += GIC_SPI_INT_BASE; + int_grp = irq / 32; + int_off = irq % 32; + + sys_write32((1 << int_off), (GICD_ICENABLERn + int_grp * 4)); +} + +static unsigned int gic_irq_get_state(struct device *dev) +{ + return 1; +} + +static void gic_irq_set_priority(struct device *dev, + unsigned int irq, unsigned int prio, u32_t flags) +{ + int int_grp, int_off; + u8_t val; + + irq += GIC_SPI_INT_BASE; + + /* Set priority */ + sys_write8(prio & 0xff, GICD_IPRIORITYRn + irq); + + /* Set interrupt type */ + int_grp = irq / 4; + int_off = (irq % 4) * 2; + + val = sys_read8(GICD_ICFGRn + int_grp); + val &= ~(GIC_INT_TYPE_MASK << int_off); + if (flags & IRQ_TYPE_EDGE) + val |= (GIC_INT_TYPE_EDGE << int_off); + sys_write8(val, GICD_ICFGRn + int_grp); +} + +static void gic_isr(void *arg) +{ + struct device *dev = arg; + const struct gic_ictl_config *cfg = dev->config->config_info; + void (*gic_isr_handle)(void *); + int irq, isr_offset; + + irq = sys_read32(GICC_IAR); + irq &= 0x3ff; + + if (irq == NO_GIC_INT_PENDING) { + printk("gic: Invalid interrupt\n"); + return; + } + + isr_offset = cfg->isr_table_offset + irq - GIC_SPI_INT_BASE; + + gic_isr_handle = _sw_isr_table[isr_offset].isr; + if (gic_isr_handle) + gic_isr_handle(_sw_isr_table[isr_offset].arg); + else + printk("gic: no handler found for int %d\n", irq); + + /* set to inactive */ + sys_write32(irq, GICC_EOIR); +} + +static int gic_init(struct device *unused); +static const struct irq_next_level_api gic_apis = { + .intr_enable = gic_irq_enable, + .intr_disable = gic_irq_disable, + .intr_get_state = gic_irq_get_state, + .intr_set_priority = gic_irq_set_priority, +}; + +static const struct gic_ictl_config gic_config = { + .isr_table_offset = CONFIG_2ND_LVL_ISR_TBL_OFFSET, +}; + +DEVICE_AND_API_INIT(arm_gic, DT_INST_0_ARM_GIC_LABEL, + gic_init, NULL, &gic_config, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &gic_apis); + +/** + * + * @brief Initialize the GIC device driver + * + * + * @return N/A + */ +#define GIC_PARENT_IRQ 0 +#define GIC_PARENT_IRQ_PRI 0 +#define GIC_PARENT_IRQ_FLAGS 0 +static int gic_init(struct device *unused) +{ + IRQ_CONNECT(GIC_PARENT_IRQ, GIC_PARENT_IRQ_PRI, gic_isr, + DEVICE_GET(arm_gic), GIC_PARENT_IRQ_FLAGS); + + /* Init of Distributor interface registers */ + gic_dist_init(); + + /* Init CPU interface registers */ + gic_cpu_init(); + + return 0; +} diff --git a/dts/bindings/interrupt-controller/arm,gic.yaml b/dts/bindings/interrupt-controller/arm,gic.yaml new file mode 100644 index 00000000000..87b7df9dde3 --- /dev/null +++ b/dts/bindings/interrupt-controller/arm,gic.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2018 Marvell +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: ARMv7-R Generic Interrupt Controller + +description: > + This binding describes the ARM Generic Interrupt Controller. + +inherits: + !include base.yaml + +properties: + compatible: + constraint: "arm,gic" + + reg: + category: required + + label: + category: required + +"#cells": + - irq + - priority + - flags +... diff --git a/include/dt-bindings/interrupt-controller/arm-gic.h b/include/dt-bindings/interrupt-controller/arm-gic.h new file mode 100644 index 00000000000..5fbba6d1095 --- /dev/null +++ b/include/dt-bindings/interrupt-controller/arm-gic.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Lexmark International, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __DT_BINDING_ARM_GIC_H +#define __DT_BINDING_ARM_GIC_H + +/* CPU Interrupt numbers */ +#define GIC_INT_VIRT_MAINT 25 +#define GIC_INT_HYP_TIMER 26 +#define GIC_INT_VIRT_TIMER 27 +#define GIC_INT_LEGACY_FIQ 28 +#define GIC_INT_PHYS_TIMER 29 +#define GIC_INT_NS_PHYS_TIMER 30 +#define GIC_INT_LEGACY_IRQ 31 + +#define IRQ_TYPE_LEVEL 0x0 +#define IRQ_TYPE_EDGE 0x1 + +#define IRQ_DEFAULT_PRIORITY 0xa + +#endif