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