18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2002 ARM Limited, All Rights Reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/irq.h>
98c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "irq-gic-common.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(irq_controller_lock);
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic const struct gic_kvm_info *gic_kvm_info;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciconst struct gic_kvm_info *gic_get_kvm_info(void)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	return gic_kvm_info;
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_civoid gic_set_kvm_info(const struct gic_kvm_info *info)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	BUG_ON(gic_kvm_info != NULL);
258c2ecf20Sopenharmony_ci	gic_kvm_info = info;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_civoid gic_enable_of_quirks(const struct device_node *np,
298c2ecf20Sopenharmony_ci			  const struct gic_quirk *quirks, void *data)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	for (; quirks->desc; quirks++) {
328c2ecf20Sopenharmony_ci		if (!quirks->compatible && !quirks->property)
338c2ecf20Sopenharmony_ci			continue;
348c2ecf20Sopenharmony_ci		if (quirks->compatible &&
358c2ecf20Sopenharmony_ci		    !of_device_is_compatible(np, quirks->compatible))
368c2ecf20Sopenharmony_ci			continue;
378c2ecf20Sopenharmony_ci		if (quirks->property &&
388c2ecf20Sopenharmony_ci		    !of_property_read_bool(np, quirks->property))
398c2ecf20Sopenharmony_ci			continue;
408c2ecf20Sopenharmony_ci		if (quirks->init(data))
418c2ecf20Sopenharmony_ci			pr_info("GIC: enabling workaround for %s\n",
428c2ecf20Sopenharmony_ci				quirks->desc);
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
478c2ecf20Sopenharmony_ci		void *data)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	for (; quirks->desc; quirks++) {
508c2ecf20Sopenharmony_ci		if (quirks->compatible || quirks->property)
518c2ecf20Sopenharmony_ci			continue;
528c2ecf20Sopenharmony_ci		if (quirks->iidr != (quirks->mask & iidr))
538c2ecf20Sopenharmony_ci			continue;
548c2ecf20Sopenharmony_ci		if (quirks->init(data))
558c2ecf20Sopenharmony_ci			pr_info("GIC: enabling workaround for %s\n",
568c2ecf20Sopenharmony_ci				quirks->desc);
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint gic_configure_irq(unsigned int irq, unsigned int type,
618c2ecf20Sopenharmony_ci		       void __iomem *base, void (*sync_access)(void))
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u32 confmask = 0x2 << ((irq % 16) * 2);
648c2ecf20Sopenharmony_ci	u32 confoff = (irq / 16) * 4;
658c2ecf20Sopenharmony_ci	u32 val, oldval;
668c2ecf20Sopenharmony_ci	int ret = 0;
678c2ecf20Sopenharmony_ci	unsigned long flags;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * Read current configuration register, and insert the config
718c2ecf20Sopenharmony_ci	 * for "irq", depending on "type".
728c2ecf20Sopenharmony_ci	 */
738c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq_controller_lock, flags);
748c2ecf20Sopenharmony_ci	val = oldval = readl_relaxed(base + confoff);
758c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_LEVEL_MASK)
768c2ecf20Sopenharmony_ci		val &= ~confmask;
778c2ecf20Sopenharmony_ci	else if (type & IRQ_TYPE_EDGE_BOTH)
788c2ecf20Sopenharmony_ci		val |= confmask;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* If the current configuration is the same, then we are done */
818c2ecf20Sopenharmony_ci	if (val == oldval) {
828c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
838c2ecf20Sopenharmony_ci		return 0;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/*
878c2ecf20Sopenharmony_ci	 * Write back the new configuration, and possibly re-enable
888c2ecf20Sopenharmony_ci	 * the interrupt. If we fail to write a new configuration for
898c2ecf20Sopenharmony_ci	 * an SPI then WARN and return an error. If we fail to write the
908c2ecf20Sopenharmony_ci	 * configuration for a PPI this is most likely because the GIC
918c2ecf20Sopenharmony_ci	 * does not allow us to set the configuration or we are in a
928c2ecf20Sopenharmony_ci	 * non-secure mode, and hence it may not be catastrophic.
938c2ecf20Sopenharmony_ci	 */
948c2ecf20Sopenharmony_ci	writel_relaxed(val, base + confoff);
958c2ecf20Sopenharmony_ci	if (readl_relaxed(base + confoff) != val)
968c2ecf20Sopenharmony_ci		ret = -EINVAL;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (sync_access)
1018c2ecf20Sopenharmony_ci		sync_access();
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid gic_dist_config(void __iomem *base, int gic_irqs,
1078c2ecf20Sopenharmony_ci		     void (*sync_access)(void))
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	unsigned int i;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/*
1128c2ecf20Sopenharmony_ci	 * Set all global interrupts to be level triggered, active low.
1138c2ecf20Sopenharmony_ci	 */
1148c2ecf20Sopenharmony_ci	for (i = 32; i < gic_irqs; i += 16)
1158c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
1168c2ecf20Sopenharmony_ci					base + GIC_DIST_CONFIG + i / 4);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/*
1198c2ecf20Sopenharmony_ci	 * Set priority on all global interrupts.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	for (i = 32; i < gic_irqs; i += 4)
1228c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * Deactivate and disable all SPIs. Leave the PPI and SGIs
1268c2ecf20Sopenharmony_ci	 * alone as they are in the redistributor registers on GICv3.
1278c2ecf20Sopenharmony_ci	 */
1288c2ecf20Sopenharmony_ci	for (i = 32; i < gic_irqs; i += 32) {
1298c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_EN_CLR_X32,
1308c2ecf20Sopenharmony_ci			       base + GIC_DIST_ACTIVE_CLEAR + i / 8);
1318c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_EN_CLR_X32,
1328c2ecf20Sopenharmony_ci			       base + GIC_DIST_ENABLE_CLEAR + i / 8);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (sync_access)
1368c2ecf20Sopenharmony_ci		sync_access();
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void))
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int i;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/*
1448c2ecf20Sopenharmony_ci	 * Deal with the banked PPI and SGI interrupts - disable all
1458c2ecf20Sopenharmony_ci	 * private interrupts. Make sure everything is deactivated.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i += 32) {
1488c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_EN_CLR_X32,
1498c2ecf20Sopenharmony_ci			       base + GIC_DIST_ACTIVE_CLEAR + i / 8);
1508c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_EN_CLR_X32,
1518c2ecf20Sopenharmony_ci			       base + GIC_DIST_ENABLE_CLEAR + i / 8);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * Set priority on PPI and SGI interrupts
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i += 4)
1588c2ecf20Sopenharmony_ci		writel_relaxed(GICD_INT_DEF_PRI_X4,
1598c2ecf20Sopenharmony_ci					base + GIC_DIST_PRI + i * 4 / 4);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (sync_access)
1628c2ecf20Sopenharmony_ci		sync_access();
1638c2ecf20Sopenharmony_ci}
164