blob: 7abb5a1c11222f20fd93642ce765a82c9017dfaa [file] [log] [blame]
/* Copyright (c) 2014, The Linux Foundation. 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.
* * Neither the name of The Linux Fundation, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* 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.
*
*/
#include <reg.h>
#include <bits.h>
#include <arch/arm.h>
#include <kernel/thread.h>
#include <platform/irqs.h>
#include <platform/iomap.h>
#include <qgic.h>
#include <debug.h>
static struct ihandler handler[NR_IRQS];
/* Intialize distributor */
void qgic_dist_config(uint32_t num_irq)
{
uint32_t i;
/* Set each interrupt line to use N-N software model
* and edge sensitive, active high
*/
for (i = 32; i < num_irq; i += 16)
writel(0xffffffff, GIC_DIST_CONFIG + i * 4 / 16);
writel(0xffffffff, GIC_DIST_CONFIG + 4);
/* Set priority of all interrupts */
/*
* In bootloader we dont care about priority so
* setting up equal priorities for all
*/
for (i = 0; i < num_irq; i += 4)
writel(0xa0a0a0a0, GIC_DIST_PRI + i * 4 / 4);
/* Disabling interrupts */
for (i = 0; i < num_irq; i += 32)
writel(0xffffffff, GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
writel(0x0000ffff, GIC_DIST_ENABLE_SET);
}
/* Initialize QGIC. Called from platform specific init code */
void qgic_init(void)
{
qgic_dist_init();
qgic_cpu_init();
}
/* IRQ handler */
enum handler_return gic_platform_irq(struct arm_iframe *frame)
{
uint32_t num;
enum handler_return ret;
/* Read the interrupt number to be served*/
num = qgic_read_iar();
if (num >= NR_IRQS)
return 0;
ret = handler[num].func(handler[num].arg);
/* End of interrupt */
qgic_write_eoi(num);
return ret;
}
/* FIQ handler */
void gic_platform_fiq(struct arm_iframe *frame)
{
PANIC_UNIMPLEMENTED;
}
/* Mask interrupt */
status_t gic_mask_interrupt(unsigned int vector)
{
uint32_t reg = GIC_DIST_ENABLE_CLEAR + (vector / 32) * 4;
uint32_t bit = 1 << (vector & 31);
writel(bit, reg);
return 0;
}
/* Un-mask interrupt */
status_t gic_unmask_interrupt(unsigned int vector)
{
uint32_t reg = GIC_DIST_ENABLE_SET + (vector / 32) * 4;
uint32_t bit = 1 << (vector & 31);
writel(bit, reg);
return 0;
}
/* Register interrupt handler */
void gic_register_int_handler(unsigned int vector, int_handler func, void *arg)
{
ASSERT(vector < NR_IRQS);
enter_critical_section();
handler[vector].func = func;
handler[vector].arg = arg;
exit_critical_section();
}
void qgic_change_interrupt_cfg(uint32_t spi_number, uint8_t type)
{
uint32_t register_number, register_address, bit_number, value;
register_number = spi_number >> 4; // r = n DIV 16
bit_number = (spi_number % 16) << 1; // b = (n MOD 16) * 2
value = readl(GIC_DIST_CONFIG + (register_number << 2));
// there are two bits per register to indicate the level
if (type == INTERRUPT_LVL_N_TO_N)
value &= ~(BIT(bit_number)|BIT(bit_number+1)); // 0x0 0x0
else if (type == INTERRUPT_LVL_1_TO_N)
value = (value & ~BIT(bit_number+1)) | BIT(bit_number); // 0x0 0x1
else if (type == INTERRUPT_EDGE_N_TO_N)
value = BIT(bit_number+1) | (value & ~BIT(bit_number));// 0x1 0x0
else if (type == INTERRUPT_EDGE_1_TO_N)
value |= (BIT(bit_number)|BIT(bit_number+1)); // 0x1 0x1
else
dprintf(CRITICAL, "Invalid interrupt type change requested\n");
register_address = GIC_DIST_CONFIG + (register_number << 2);
writel(value, register_address);
}