mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
0eece2b228
When booting a GIC/GICv3 based system, we have no idea what state the firmware (or previous kernel in the case of kexec) has left the GIC, and some interrupts may still be active. In order to garantee that we have a clean state, make sure the active bits are cleared at init time. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Cc: <linux-arm-kernel@lists.infradead.org> Cc: Jason Cooper <jason@lakedaemon.net> Cc: Russell King <linux@arm.linux.org.uk> Link: http://lkml.kernel.org/r/1447701208-18150-3-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
124 lines
3.3 KiB
C
124 lines
3.3 KiB
C
/*
|
|
* Copyright (C) 2002 ARM Limited, All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip/arm-gic.h>
|
|
|
|
#include "irq-gic-common.h"
|
|
|
|
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
|
|
void *data)
|
|
{
|
|
for (; quirks->desc; quirks++) {
|
|
if (quirks->iidr != (quirks->mask & iidr))
|
|
continue;
|
|
quirks->init(data);
|
|
pr_info("GIC: enabling workaround for %s\n", quirks->desc);
|
|
}
|
|
}
|
|
|
|
int gic_configure_irq(unsigned int irq, unsigned int type,
|
|
void __iomem *base, void (*sync_access)(void))
|
|
{
|
|
u32 confmask = 0x2 << ((irq % 16) * 2);
|
|
u32 confoff = (irq / 16) * 4;
|
|
u32 val, oldval;
|
|
int ret = 0;
|
|
|
|
/*
|
|
* Read current configuration register, and insert the config
|
|
* for "irq", depending on "type".
|
|
*/
|
|
val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
|
|
if (type & IRQ_TYPE_LEVEL_MASK)
|
|
val &= ~confmask;
|
|
else if (type & IRQ_TYPE_EDGE_BOTH)
|
|
val |= confmask;
|
|
|
|
/*
|
|
* Write back the new configuration, and possibly re-enable
|
|
* the interrupt. If we tried to write a new configuration and failed,
|
|
* return an error.
|
|
*/
|
|
writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
|
|
if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
|
|
ret = -EINVAL;
|
|
|
|
if (sync_access)
|
|
sync_access();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __init gic_dist_config(void __iomem *base, int gic_irqs,
|
|
void (*sync_access)(void))
|
|
{
|
|
unsigned int i;
|
|
|
|
/*
|
|
* Set all global interrupts to be level triggered, active low.
|
|
*/
|
|
for (i = 32; i < gic_irqs; i += 16)
|
|
writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
|
|
base + GIC_DIST_CONFIG + i / 4);
|
|
|
|
/*
|
|
* Set priority on all global interrupts.
|
|
*/
|
|
for (i = 32; i < gic_irqs; i += 4)
|
|
writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
|
|
|
|
/*
|
|
* Deactivate and disable all SPIs. Leave the PPI and SGIs
|
|
* alone as they are in the redistributor registers on GICv3.
|
|
*/
|
|
for (i = 32; i < gic_irqs; i += 32) {
|
|
writel_relaxed(GICD_INT_EN_CLR_X32,
|
|
base + GIC_DIST_ACTIVE_CLEAR + i / 8);
|
|
writel_relaxed(GICD_INT_EN_CLR_X32,
|
|
base + GIC_DIST_ENABLE_CLEAR + i / 8);
|
|
}
|
|
|
|
if (sync_access)
|
|
sync_access();
|
|
}
|
|
|
|
void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* Deal with the banked PPI and SGI interrupts - disable all
|
|
* PPI interrupts, ensure all SGI interrupts are enabled.
|
|
* Make sure everything is deactivated.
|
|
*/
|
|
writel_relaxed(GICD_INT_EN_CLR_X32, base + GIC_DIST_ACTIVE_CLEAR);
|
|
writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
|
|
writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
|
|
|
|
/*
|
|
* Set priority on PPI and SGI interrupts
|
|
*/
|
|
for (i = 0; i < 32; i += 4)
|
|
writel_relaxed(GICD_INT_DEF_PRI_X4,
|
|
base + GIC_DIST_PRI + i * 4 / 4);
|
|
|
|
if (sync_access)
|
|
sync_access();
|
|
}
|