interrupt_controller: gic: Refactor GIC driver interface

The current Generic Interrupt Controller (GIC) driver makes use of the
multi-level interrupt mechanism and `irq_nextlevel` public interface.

This is a less-than-ideal implementation for the following reasons:

1. The GIC is often used as the main interrupt controller for the
  Cortex-A and Cortex-R family SoCs and, in this case, it is not a 2nd
  level interrupt controller; in fact, it is the root interrupt
  controller and therefore should be treated as such.

2. The only reason for using `irq_nextlevel` here is to interface the
  architecture implementation to the interrupt controller functions.
  Since there is no nesting or multiple instances of an interrupt
  controller involved, there is really no point in adding such an
  abstraction.

3. 2nd level topology adds many unnecessary abstractions and results
  in strange coding artefacts as well as performance penalty due to
  additional branching.

This commit refactors the GIC driver interface as follows:

1. Remove the current GIC driver interface based on the multi-level
  interrupt mechanism and the `irq_nextlevel` public interface.

2. Define the GIC driver interface in
  `include/drivers/interrupt_controller/gic.h` and allow the arch
  implementation to directly invoke this interface.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
This commit is contained in:
Stephanos Ioannidis 2020-02-11 15:47:59 +09:00 committed by Ioannis Glaropoulos
commit 50519ce7ba
2 changed files with 153 additions and 104 deletions

View file

@ -11,9 +11,7 @@
* GICv3 interface is not supported.
*/
#include <device.h>
#include <sw_isr_table.h>
#include <irq_nextlevel.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <drivers/interrupt_controller/gic.h>
@ -21,9 +19,74 @@
#error "GICv3 and above are not supported"
#endif
struct gic_ictl_config {
u32_t isr_table_offset;
};
void arm_gic_irq_enable(unsigned int irq)
{
int int_grp, int_off;
int_grp = irq / 32;
int_off = irq % 32;
sys_write32((1 << int_off), (GICD_ISENABLERn + int_grp * 4));
}
void arm_gic_irq_disable(unsigned int irq)
{
int int_grp, int_off;
int_grp = irq / 32;
int_off = irq % 32;
sys_write32((1 << int_off), (GICD_ICENABLERn + int_grp * 4));
}
bool arm_gic_irq_is_enabled(unsigned int irq)
{
int int_grp, int_off;
unsigned int enabler;
int_grp = irq / 32;
int_off = irq % 32;
enabler = sys_read32(GICD_ISENABLERn + int_grp * 4);
return (enabler & (1 << int_off)) != 0;
}
void arm_gic_irq_set_priority(
unsigned int irq, unsigned int prio, u32_t flags)
{
int int_grp, int_off;
u8_t val;
/* Set priority */
sys_write8(prio & 0xff, GICD_IPRIORITYRn + irq);
/* Set interrupt type */
int_grp = irq / 4;
int_off = (irq % 16) * 2;
val = sys_read8(GICD_ICFGRn + int_grp);
val &= ~(GICC_ICFGR_MASK << int_off);
if (flags & IRQ_TYPE_EDGE) {
val |= (GICC_ICFGR_TYPE << int_off);
}
sys_write8(val, GICD_ICFGRn + int_grp);
}
unsigned int arm_gic_get_active(void)
{
int irq;
irq = sys_read32(GICC_IAR) & 0x3ff;
return irq;
}
void arm_gic_eoi(unsigned int irq)
{
/* set to inactive */
sys_write32(irq, GICC_EOIR);
}
static void gic_dist_init(void)
{
@ -31,8 +94,9 @@ static void gic_dist_init(void)
gic_irqs = sys_read32(GICD_TYPER) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
if (gic_irqs > 1020) {
gic_irqs = 1020;
}
/*
* Disable the forwarding of pending interrupts
@ -43,22 +107,26 @@ static void gic_dist_init(void)
/*
* Set all global interrupts to this CPU only.
*/
for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 4)
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)
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)
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)
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
@ -96,8 +164,9 @@ static void gic_cpu_init(void)
/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4)
for (i = 0; i < 32; i += 4) {
sys_write32(0xa0a0a0a0, GICD_IPRIORITYRn + i);
}
sys_write32(0xf0, GICC_PMR);
@ -112,94 +181,6 @@ static void gic_cpu_init(void)
sys_write32(val, GICC_CTLR);
}
static void gic_irq_enable(struct device *dev, unsigned int irq)
{
int int_grp, int_off;
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;
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;
/* Set priority */
sys_write8(prio & 0xff, GICD_IPRIORITYRn + irq);
/* Set interrupt type */
int_grp = irq / 4;
int_off = (irq % 16) * 2;
val = sys_read8(GICD_ICFGRn + int_grp);
val &= ~(GICC_ICFGR_MASK << int_off);
if (flags & IRQ_TYPE_EDGE)
val |= (GICC_ICFGR_TYPE << 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 == GICC_IAR_SPURIOUS) {
printk("gic: Invalid interrupt\n");
return;
}
isr_offset = cfg->isr_table_offset + irq;
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
@ -210,11 +191,8 @@ DEVICE_AND_API_INIT(arm_gic, DT_INST_0_ARM_GIC_LABEL,
#define GIC_PARENT_IRQ 0
#define GIC_PARENT_IRQ_PRI 0
#define GIC_PARENT_IRQ_FLAGS 0
static int gic_init(struct device *unused)
int arm_gic_init(void)
{
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();

View file

@ -4,10 +4,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Driver for ARM Generic Interrupt Controller
*
* The Generic Interrupt Controller (GIC) is the default interrupt controller
* for the ARM A and R profile cores. This driver is used by the ARM arch
* implementation to handle interrupts.
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_GIC_H_
#define ZEPHYR_INCLUDE_DRIVERS_GIC_H_
#include <arch/cpu.h>
#include <zephyr/types.h>
#include <device.h>
/*
* GIC Register Interface Base Addresses
@ -196,4 +206,65 @@
#endif /* CONFIG_GIC_VER <= 2 */
#ifndef _ASMLANGUAGE
/*
* GIC Driver Interface Functions
*/
/**
* @brief Initialise ARM GIC driver
*
* @return 0 if successful
*/
int arm_gic_init(void);
/**
* @brief Enable interrupt
*
* @param irq interrupt ID
*/
void arm_gic_irq_enable(unsigned int irq);
/**
* @brief Disable interrupt
*
* @param irq interrupt ID
*/
void arm_gic_irq_disable(unsigned int irq);
/**
* @brief Check if an interrupt is enabled
*
* @param irq interrupt ID
* @return Returns true if interrupt is enabled, false otherwise
*/
bool arm_gic_irq_is_enabled(unsigned int irq);
/**
* @brief Set interrupt priority
*
* @param irq interrupt ID
* @param prio interrupt priority
* @param flags interrupt flags
*/
void arm_gic_irq_set_priority(
unsigned int irq, unsigned int prio, unsigned int flags);
/**
* @brief Get active interrupt ID
*
* @return Returns the ID of an active interrupt
*/
unsigned int arm_gic_get_active(void);
/**
* @brief Signal end-of-interrupt
*
* @param irq interrupt ID
*/
void arm_gic_eoi(unsigned int irq);
#endif /* !_ASMLANGUAGE */
#endif /* ZEPHYR_INCLUDE_DRIVERS_GIC_H_ */