162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002 ARM Limited, All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/irq.h> 962306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "irq-gic-common.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(irq_controller_lock); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_civoid gic_enable_of_quirks(const struct device_node *np, 1662306a36Sopenharmony_ci const struct gic_quirk *quirks, void *data) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci for (; quirks->desc; quirks++) { 1962306a36Sopenharmony_ci if (!quirks->compatible && !quirks->property) 2062306a36Sopenharmony_ci continue; 2162306a36Sopenharmony_ci if (quirks->compatible && 2262306a36Sopenharmony_ci !of_device_is_compatible(np, quirks->compatible)) 2362306a36Sopenharmony_ci continue; 2462306a36Sopenharmony_ci if (quirks->property && 2562306a36Sopenharmony_ci !of_property_read_bool(np, quirks->property)) 2662306a36Sopenharmony_ci continue; 2762306a36Sopenharmony_ci if (quirks->init(data)) 2862306a36Sopenharmony_ci pr_info("GIC: enabling workaround for %s\n", 2962306a36Sopenharmony_ci quirks->desc); 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, 3462306a36Sopenharmony_ci void *data) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci for (; quirks->desc; quirks++) { 3762306a36Sopenharmony_ci if (quirks->compatible || quirks->property) 3862306a36Sopenharmony_ci continue; 3962306a36Sopenharmony_ci if (quirks->iidr != (quirks->mask & iidr)) 4062306a36Sopenharmony_ci continue; 4162306a36Sopenharmony_ci if (quirks->init(data)) 4262306a36Sopenharmony_ci pr_info("GIC: enabling workaround for %s\n", 4362306a36Sopenharmony_ci quirks->desc); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint gic_configure_irq(unsigned int irq, unsigned int type, 4862306a36Sopenharmony_ci void __iomem *base, void (*sync_access)(void)) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci u32 confmask = 0x2 << ((irq % 16) * 2); 5162306a36Sopenharmony_ci u32 confoff = (irq / 16) * 4; 5262306a36Sopenharmony_ci u32 val, oldval; 5362306a36Sopenharmony_ci int ret = 0; 5462306a36Sopenharmony_ci unsigned long flags; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Read current configuration register, and insert the config 5862306a36Sopenharmony_ci * for "irq", depending on "type". 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci raw_spin_lock_irqsave(&irq_controller_lock, flags); 6162306a36Sopenharmony_ci val = oldval = readl_relaxed(base + confoff); 6262306a36Sopenharmony_ci if (type & IRQ_TYPE_LEVEL_MASK) 6362306a36Sopenharmony_ci val &= ~confmask; 6462306a36Sopenharmony_ci else if (type & IRQ_TYPE_EDGE_BOTH) 6562306a36Sopenharmony_ci val |= confmask; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* If the current configuration is the same, then we are done */ 6862306a36Sopenharmony_ci if (val == oldval) { 6962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * Write back the new configuration, and possibly re-enable 7562306a36Sopenharmony_ci * the interrupt. If we fail to write a new configuration for 7662306a36Sopenharmony_ci * an SPI then WARN and return an error. If we fail to write the 7762306a36Sopenharmony_ci * configuration for a PPI this is most likely because the GIC 7862306a36Sopenharmony_ci * does not allow us to set the configuration or we are in a 7962306a36Sopenharmony_ci * non-secure mode, and hence it may not be catastrophic. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci writel_relaxed(val, base + confoff); 8262306a36Sopenharmony_ci if (readl_relaxed(base + confoff) != val) 8362306a36Sopenharmony_ci ret = -EINVAL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (sync_access) 8862306a36Sopenharmony_ci sync_access(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid gic_dist_config(void __iomem *base, int gic_irqs, 9462306a36Sopenharmony_ci void (*sync_access)(void)) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned int i; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Set all global interrupts to be level triggered, active low. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci for (i = 32; i < gic_irqs; i += 16) 10262306a36Sopenharmony_ci writel_relaxed(GICD_INT_ACTLOW_LVLTRIG, 10362306a36Sopenharmony_ci base + GIC_DIST_CONFIG + i / 4); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Set priority on all global interrupts. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci for (i = 32; i < gic_irqs; i += 4) 10962306a36Sopenharmony_ci writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Deactivate and disable all SPIs. Leave the PPI and SGIs 11362306a36Sopenharmony_ci * alone as they are in the redistributor registers on GICv3. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci for (i = 32; i < gic_irqs; i += 32) { 11662306a36Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 11762306a36Sopenharmony_ci base + GIC_DIST_ACTIVE_CLEAR + i / 8); 11862306a36Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 11962306a36Sopenharmony_ci base + GIC_DIST_ENABLE_CLEAR + i / 8); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (sync_access) 12362306a36Sopenharmony_ci sync_access(); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_civoid gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void)) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int i; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Deal with the banked PPI and SGI interrupts - disable all 13262306a36Sopenharmony_ci * private interrupts. Make sure everything is deactivated. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci for (i = 0; i < nr; i += 32) { 13562306a36Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 13662306a36Sopenharmony_ci base + GIC_DIST_ACTIVE_CLEAR + i / 8); 13762306a36Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 13862306a36Sopenharmony_ci base + GIC_DIST_ENABLE_CLEAR + i / 8); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Set priority on PPI and SGI interrupts 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci for (i = 0; i < nr; i += 4) 14562306a36Sopenharmony_ci writel_relaxed(GICD_INT_DEF_PRI_X4, 14662306a36Sopenharmony_ci base + GIC_DIST_PRI + i * 4 / 4); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (sync_access) 14962306a36Sopenharmony_ci sync_access(); 15062306a36Sopenharmony_ci} 151