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:
parent
67527ce4b7
commit
50519ce7ba
2 changed files with 153 additions and 104 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue