/*
* Copyright (c) 2016 Petr Pavlu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup genarch
* @{
*/
/** @file
* @brief ARM Generic Interrupt Controller, Architecture version 2.0.
*
* This IRQ controller is present on the QEMU virt platform for ARM.
*/
#include <arch/asm.h>
#include <genarch/drivers/gicv2/gicv2.h>
#include <assert.h>
/** Initialize GICv2 interrupt controller.
*
* @param irqc Instance structure.
* @param distr Distributor registers.
* @param cpui CPU interface registers.
*/
void gicv2_init(gicv2_t *irqc, gicv2_distr_regs_t *distr,
gicv2_cpui_regs_t *cpui)
{
irqc->distr = distr;
irqc->cpui = cpui;
/* Get maximum number of interrupts. */
uint32_t typer = pio_read_32(&distr->typer);
irqc->inum_total = (((typer & GICV2D_TYPER_IT_LINES_NUMBER_MASK) >>
GICV2D_TYPER_IT_LINES_NUMBER_SHIFT) + 1) * 32;
/* Disable all interrupts. */
for (unsigned i = 0; i < irqc->inum_total / 32; i++)
pio_write_32(&distr->icenabler[i], 0xffffffff);
/* Enable interrupts for all priority levels. */
pio_write_32(&cpui->pmr, 0xff);
/* Enable signaling of interrupts. */
pio_write_32(&cpui->ctlr, GICV2C_CTLR_ENABLE_FLAG);
pio_write_32(&distr->ctlr, GICV2D_CTLR_ENABLE_FLAG);
}
/** Obtain total number of interrupts that the controller supports. */
unsigned gicv2_inum_get_total(gicv2_t *irqc)
{
return irqc->inum_total;
}
/** Obtain number of pending interrupt. */
void gicv2_inum_get(gicv2_t *irqc, unsigned *inum, unsigned *cpuid)
{
uint32_t iar = pio_read_32(&irqc->cpui->iar);
*inum = (iar & GICV2C_IAR_INTERRUPT_ID_MASK) >>
GICV2C_IAR_INTERRUPT_ID_SHIFT;
*cpuid = (iar & GICV2C_IAR_CPUID_MASK) >> GICV2C_IAR_CPUID_SHIFT;
}
/** Signal end of interrupt to the controller. */
void gicv2_end(gicv2_t *irqc, unsigned inum, unsigned cpuid)
{
assert((inum & ~((unsigned) GICV2C_IAR_INTERRUPT_ID_MASK >>
GICV2C_IAR_INTERRUPT_ID_SHIFT)) == 0);
assert((cpuid & ~((unsigned) GICV2C_IAR_CPUID_MASK >>
GICV2C_IAR_CPUID_SHIFT)) == 0);
uint32_t eoir = (inum << GICV2C_IAR_INTERRUPT_ID_SHIFT) |
(cpuid << GICV2C_IAR_CPUID_SHIFT);
pio_write_32(&irqc->cpui->eoir, eoir);
}
/** Enable specific interrupt. */
void gicv2_enable(gicv2_t *irqc, unsigned inum)
{
assert(inum < irqc->inum_total);
pio_write_32(&irqc->distr->isenabler[inum / 32], 1 << (inum % 32));
}
/** Disable specific interrupt. */
void gicv2_disable(gicv2_t *irqc, unsigned inum)
{
assert(inum < irqc->inum_total);
pio_write_32(&irqc->distr->icenabler[inum / 32], 1 << (inum % 32));
}
/** @}
*/