zephyr/drivers/interrupt_controller/gic-400.c
Carlo Caione 7baf3f74a9 interrupt_controller: gic: Support PPIs
The GIC-400 driver currently only supports SPIs because the (32) offset
for the INTIDs is hard-coded in the driver. At the driver level there is
no really difference between PPIs and SPIs so we can easily extend the
driver to support PPIs as well.

This is useful if we want to add support for the ARM Generic Timers that
use INTIDs in the PPI range.

SPI interrupts are in the range [0-987]. PPI interrupts are in the range
[0-15].

This commit adds interrupt 'type' cell to the GIC device tree binding
and changes the 'irq' cell to use interrupt type-specific index, rather
than a linear IRQ number.

The 'type'+'irq (index)' combo is automatically fixed up into a linear
IRQ number by the scripts/dts/gen_defines.py script.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
2019-12-10 06:59:55 +01:00

243 lines
5.9 KiB
C

/*
* 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;
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 &= ~(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_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;
}