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>
This commit is contained in:
parent
b34b00d6ec
commit
571d3b54db
5 changed files with 306 additions and 0 deletions
|
@ -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"
|
||||
|
|
246
drivers/interrupt_controller/gic-400.c
Normal file
246
drivers/interrupt_controller/gic-400.c
Normal file
|
@ -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;
|
||||
}
|
29
dts/bindings/interrupt-controller/arm,gic.yaml
Normal file
29
dts/bindings/interrupt-controller/arm,gic.yaml
Normal file
|
@ -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
|
||||
...
|
23
include/dt-bindings/interrupt-controller/arm-gic.h
Normal file
23
include/dt-bindings/interrupt-controller/arm-gic.h
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue