1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2d51d0af4SMarc Zyngier /* 3d51d0af4SMarc Zyngier * Copyright (C) 2002 ARM Limited, All Rights Reserved. 4d51d0af4SMarc Zyngier */ 5d51d0af4SMarc Zyngier 6d51d0af4SMarc Zyngier #include <linux/interrupt.h> 7d51d0af4SMarc Zyngier #include <linux/io.h> 8d51d0af4SMarc Zyngier #include <linux/irq.h> 9d51d0af4SMarc Zyngier #include <linux/irqchip/arm-gic.h> 10d51d0af4SMarc Zyngier 11d51d0af4SMarc Zyngier #include "irq-gic-common.h" 12d51d0af4SMarc Zyngier 13aa08192aSAniruddha Banerjee static DEFINE_RAW_SPINLOCK(irq_controller_lock); 14aa08192aSAniruddha Banerjee 15502d6df1SJulien Grall static const struct gic_kvm_info *gic_kvm_info; 16502d6df1SJulien Grall 17502d6df1SJulien Grall const struct gic_kvm_info *gic_get_kvm_info(void) 18502d6df1SJulien Grall { 19502d6df1SJulien Grall return gic_kvm_info; 20502d6df1SJulien Grall } 21502d6df1SJulien Grall 22502d6df1SJulien Grall void gic_set_kvm_info(const struct gic_kvm_info *info) 23502d6df1SJulien Grall { 24502d6df1SJulien Grall BUG_ON(gic_kvm_info != NULL); 25502d6df1SJulien Grall gic_kvm_info = info; 26502d6df1SJulien Grall } 27502d6df1SJulien Grall 28*f70fdb42SSrinivas Kandagatla void gic_enable_of_quirks(const struct device_node *np, 29*f70fdb42SSrinivas Kandagatla const struct gic_quirk *quirks, void *data) 30*f70fdb42SSrinivas Kandagatla { 31*f70fdb42SSrinivas Kandagatla for (; quirks->desc; quirks++) { 32*f70fdb42SSrinivas Kandagatla if (!of_device_is_compatible(np, quirks->compatible)) 33*f70fdb42SSrinivas Kandagatla continue; 34*f70fdb42SSrinivas Kandagatla if (quirks->init(data)) 35*f70fdb42SSrinivas Kandagatla pr_info("GIC: enabling workaround for %s\n", 36*f70fdb42SSrinivas Kandagatla quirks->desc); 37*f70fdb42SSrinivas Kandagatla } 38*f70fdb42SSrinivas Kandagatla } 39*f70fdb42SSrinivas Kandagatla 4067510ccaSRobert Richter void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, 4167510ccaSRobert Richter void *data) 4267510ccaSRobert Richter { 4367510ccaSRobert Richter for (; quirks->desc; quirks++) { 4425edaed6SMarc Zyngier if (quirks->compatible) 4525edaed6SMarc Zyngier continue; 4667510ccaSRobert Richter if (quirks->iidr != (quirks->mask & iidr)) 4767510ccaSRobert Richter continue; 489d111d49SArd Biesheuvel if (quirks->init(data)) 499d111d49SArd Biesheuvel pr_info("GIC: enabling workaround for %s\n", 509d111d49SArd Biesheuvel quirks->desc); 5167510ccaSRobert Richter } 5267510ccaSRobert Richter } 5367510ccaSRobert Richter 54fb7e7debSLiviu Dudau int gic_configure_irq(unsigned int irq, unsigned int type, 55d51d0af4SMarc Zyngier void __iomem *base, void (*sync_access)(void)) 56d51d0af4SMarc Zyngier { 57d51d0af4SMarc Zyngier u32 confmask = 0x2 << ((irq % 16) * 2); 58d51d0af4SMarc Zyngier u32 confoff = (irq / 16) * 4; 59fb7e7debSLiviu Dudau u32 val, oldval; 60fb7e7debSLiviu Dudau int ret = 0; 61aa08192aSAniruddha Banerjee unsigned long flags; 62d51d0af4SMarc Zyngier 63d51d0af4SMarc Zyngier /* 64d51d0af4SMarc Zyngier * Read current configuration register, and insert the config 65d51d0af4SMarc Zyngier * for "irq", depending on "type". 66d51d0af4SMarc Zyngier */ 67aa08192aSAniruddha Banerjee raw_spin_lock_irqsave(&irq_controller_lock, flags); 6813d22e2eSMarc Zyngier val = oldval = readl_relaxed(base + confoff); 69fb7e7debSLiviu Dudau if (type & IRQ_TYPE_LEVEL_MASK) 70d51d0af4SMarc Zyngier val &= ~confmask; 71fb7e7debSLiviu Dudau else if (type & IRQ_TYPE_EDGE_BOTH) 72d51d0af4SMarc Zyngier val |= confmask; 73d51d0af4SMarc Zyngier 74ec1a454dSJon Hunter /* If the current configuration is the same, then we are done */ 75aa08192aSAniruddha Banerjee if (val == oldval) { 76aa08192aSAniruddha Banerjee raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 77ec1a454dSJon Hunter return 0; 78aa08192aSAniruddha Banerjee } 79ec1a454dSJon Hunter 80d51d0af4SMarc Zyngier /* 81d51d0af4SMarc Zyngier * Write back the new configuration, and possibly re-enable 82992345a5SJon Hunter * the interrupt. If we fail to write a new configuration for 83992345a5SJon Hunter * an SPI then WARN and return an error. If we fail to write the 84992345a5SJon Hunter * configuration for a PPI this is most likely because the GIC 85992345a5SJon Hunter * does not allow us to set the configuration or we are in a 86992345a5SJon Hunter * non-secure mode, and hence it may not be catastrophic. 87d51d0af4SMarc Zyngier */ 8813d22e2eSMarc Zyngier writel_relaxed(val, base + confoff); 8913d22e2eSMarc Zyngier if (readl_relaxed(base + confoff) != val) 90fb7e7debSLiviu Dudau ret = -EINVAL; 9113d22e2eSMarc Zyngier 92aa08192aSAniruddha Banerjee raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 93d51d0af4SMarc Zyngier 94d51d0af4SMarc Zyngier if (sync_access) 95d51d0af4SMarc Zyngier sync_access(); 96fb7e7debSLiviu Dudau 97fb7e7debSLiviu Dudau return ret; 98d51d0af4SMarc Zyngier } 99d51d0af4SMarc Zyngier 100cdbb813dSJon Hunter void gic_dist_config(void __iomem *base, int gic_irqs, 101d51d0af4SMarc Zyngier void (*sync_access)(void)) 102d51d0af4SMarc Zyngier { 103d51d0af4SMarc Zyngier unsigned int i; 104d51d0af4SMarc Zyngier 105d51d0af4SMarc Zyngier /* 106d51d0af4SMarc Zyngier * Set all global interrupts to be level triggered, active low. 107d51d0af4SMarc Zyngier */ 108d51d0af4SMarc Zyngier for (i = 32; i < gic_irqs; i += 16) 109e5f81539SFeng Kan writel_relaxed(GICD_INT_ACTLOW_LVLTRIG, 110e5f81539SFeng Kan base + GIC_DIST_CONFIG + i / 4); 111d51d0af4SMarc Zyngier 112d51d0af4SMarc Zyngier /* 113d51d0af4SMarc Zyngier * Set priority on all global interrupts. 114d51d0af4SMarc Zyngier */ 115d51d0af4SMarc Zyngier for (i = 32; i < gic_irqs; i += 4) 116e5f81539SFeng Kan writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i); 117d51d0af4SMarc Zyngier 118d51d0af4SMarc Zyngier /* 1190eece2b2SMarc Zyngier * Deactivate and disable all SPIs. Leave the PPI and SGIs 1200eece2b2SMarc Zyngier * alone as they are in the redistributor registers on GICv3. 121d51d0af4SMarc Zyngier */ 1220eece2b2SMarc Zyngier for (i = 32; i < gic_irqs; i += 32) { 1230eece2b2SMarc Zyngier writel_relaxed(GICD_INT_EN_CLR_X32, 1240eece2b2SMarc Zyngier base + GIC_DIST_ACTIVE_CLEAR + i / 8); 125e5f81539SFeng Kan writel_relaxed(GICD_INT_EN_CLR_X32, 126e5f81539SFeng Kan base + GIC_DIST_ENABLE_CLEAR + i / 8); 1270eece2b2SMarc Zyngier } 128d51d0af4SMarc Zyngier 129d51d0af4SMarc Zyngier if (sync_access) 130d51d0af4SMarc Zyngier sync_access(); 131d51d0af4SMarc Zyngier } 132d51d0af4SMarc Zyngier 1331a60e1e6SMarc Zyngier void gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void)) 134d51d0af4SMarc Zyngier { 135d51d0af4SMarc Zyngier int i; 136d51d0af4SMarc Zyngier 137d51d0af4SMarc Zyngier /* 138d51d0af4SMarc Zyngier * Deal with the banked PPI and SGI interrupts - disable all 1391a60e1e6SMarc Zyngier * private interrupts. Make sure everything is deactivated. 140d51d0af4SMarc Zyngier */ 1411a60e1e6SMarc Zyngier for (i = 0; i < nr; i += 32) { 1421a60e1e6SMarc Zyngier writel_relaxed(GICD_INT_EN_CLR_X32, 1431a60e1e6SMarc Zyngier base + GIC_DIST_ACTIVE_CLEAR + i / 8); 1441a60e1e6SMarc Zyngier writel_relaxed(GICD_INT_EN_CLR_X32, 1451a60e1e6SMarc Zyngier base + GIC_DIST_ENABLE_CLEAR + i / 8); 1461a60e1e6SMarc Zyngier } 147d51d0af4SMarc Zyngier 148d51d0af4SMarc Zyngier /* 149d51d0af4SMarc Zyngier * Set priority on PPI and SGI interrupts 150d51d0af4SMarc Zyngier */ 1511a60e1e6SMarc Zyngier for (i = 0; i < nr; i += 4) 152e5f81539SFeng Kan writel_relaxed(GICD_INT_DEF_PRI_X4, 153e5f81539SFeng Kan base + GIC_DIST_PRI + i * 4 / 4); 154d51d0af4SMarc Zyngier 155d51d0af4SMarc Zyngier if (sync_access) 156d51d0af4SMarc Zyngier sync_access(); 157d51d0af4SMarc Zyngier } 158