Skip to content
Permalink
Browse files

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 <bbolen@lexmark.com>
  • Loading branch information...
Bradley Bolen authored and ioannisg committed Apr 18, 2018
1 parent b34b00d commit 571d3b54dbaa28f75482aaa7cf27ebaac53eeac4
@@ -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)
@@ -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"
@@ -0,0 +1,246 @@
/*
* Copyright (c) 2018 Marvell
* Copyright (c) 2018 Lexmark International, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <device.h>
#include <sw_isr_table.h>
#include <irq_nextlevel.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>

#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;
}
@@ -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
...
@@ -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

0 comments on commit 571d3b5

Please sign in to comment.
You can’t perform that action at this time.