162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2011 IBM Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/threads.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/irq.h>
962306a36Sopenharmony_ci#include <linux/irqdomain.h>
1062306a36Sopenharmony_ci#include <linux/debugfs.h>
1162306a36Sopenharmony_ci#include <linux/smp.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/seq_file.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/cpu.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/io.h>
2262306a36Sopenharmony_ci#include <asm/smp.h>
2362306a36Sopenharmony_ci#include <asm/machdep.h>
2462306a36Sopenharmony_ci#include <asm/irq.h>
2562306a36Sopenharmony_ci#include <asm/errno.h>
2662306a36Sopenharmony_ci#include <asm/rtas.h>
2762306a36Sopenharmony_ci#include <asm/xics.h>
2862306a36Sopenharmony_ci#include <asm/firmware.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Globals common to all ICP/ICS implementations */
3162306a36Sopenharmony_ciconst struct icp_ops	*icp_ops;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciunsigned int xics_default_server		= 0xff;
3462306a36Sopenharmony_ciunsigned int xics_default_distrib_server	= 0;
3562306a36Sopenharmony_ciunsigned int xics_interrupt_server_size		= 8;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciDEFINE_PER_CPU(struct xics_cppr, xics_cppr);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct irq_domain *xics_host;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic struct ics *xics_ics;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_civoid xics_update_irq_servers(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	int i, j;
4662306a36Sopenharmony_ci	struct device_node *np;
4762306a36Sopenharmony_ci	u32 ilen;
4862306a36Sopenharmony_ci	const __be32 *ireg;
4962306a36Sopenharmony_ci	u32 hcpuid;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Find the server numbers for the boot cpu. */
5262306a36Sopenharmony_ci	np = of_get_cpu_node(boot_cpuid, NULL);
5362306a36Sopenharmony_ci	BUG_ON(!np);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	hcpuid = get_hard_smp_processor_id(boot_cpuid);
5662306a36Sopenharmony_ci	xics_default_server = xics_default_distrib_server = hcpuid;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	pr_devel("xics: xics_default_server = 0x%x\n", xics_default_server);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
6162306a36Sopenharmony_ci	if (!ireg) {
6262306a36Sopenharmony_ci		of_node_put(np);
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	i = ilen / sizeof(int);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Global interrupt distribution server is specified in the last
6962306a36Sopenharmony_ci	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
7062306a36Sopenharmony_ci	 * entry fom this property for current boot cpu id and use it as
7162306a36Sopenharmony_ci	 * default distribution server
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	for (j = 0; j < i; j += 2) {
7462306a36Sopenharmony_ci		if (be32_to_cpu(ireg[j]) == hcpuid) {
7562306a36Sopenharmony_ci			xics_default_distrib_server = be32_to_cpu(ireg[j+1]);
7662306a36Sopenharmony_ci			break;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	pr_devel("xics: xics_default_distrib_server = 0x%x\n",
8062306a36Sopenharmony_ci		 xics_default_distrib_server);
8162306a36Sopenharmony_ci	of_node_put(np);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* GIQ stuff, currently only supported on RTAS setups, will have
8562306a36Sopenharmony_ci * to be sorted properly for bare metal
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_civoid xics_set_cpu_giq(unsigned int gserver, unsigned int join)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci#ifdef CONFIG_PPC_RTAS
9062306a36Sopenharmony_ci	int index;
9162306a36Sopenharmony_ci	int status;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL))
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	index = (1UL << xics_interrupt_server_size) - 1 - gserver;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n",
10162306a36Sopenharmony_ci	     GLOBAL_INTERRUPT_QUEUE, index, join, status);
10262306a36Sopenharmony_ci#endif
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid xics_setup_cpu(void)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	icp_ops->set_priority(LOWEST_PRIORITY);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	xics_set_cpu_giq(xics_default_distrib_server, 1);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid xics_mask_unknown_vec(unsigned int vec)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	pr_err("Interrupt 0x%x (real) is invalid, disabling it.\n", vec);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (WARN_ON(!xics_ics))
11762306a36Sopenharmony_ci		return;
11862306a36Sopenharmony_ci	xics_ics->mask_unknown(xics_ics, vec);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#ifdef CONFIG_SMP
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void __init xics_request_ipi(void)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	unsigned int ipi;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ipi = irq_create_mapping(xics_host, XICS_IPI);
12962306a36Sopenharmony_ci	BUG_ON(!ipi);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * IPIs are marked IRQF_PERCPU. The handler was set in map.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	BUG_ON(request_irq(ipi, icp_ops->ipi_action,
13562306a36Sopenharmony_ci			   IRQF_NO_DEBUG | IRQF_PERCPU | IRQF_NO_THREAD, "IPI", NULL));
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid __init xics_smp_probe(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	/* Register all the IPIs */
14162306a36Sopenharmony_ci	xics_request_ipi();
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Setup cause_ipi callback based on which ICP is used */
14462306a36Sopenharmony_ci	smp_ops->cause_ipi = icp_ops->cause_ipi;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#endif /* CONFIG_SMP */
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cinoinstr void xics_teardown_cpu(void)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct xics_cppr *os_cppr = this_cpu_ptr(&xics_cppr);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * we have to reset the cppr index to 0 because we're
15562306a36Sopenharmony_ci	 * not going to return from the IPI
15662306a36Sopenharmony_ci	 */
15762306a36Sopenharmony_ci	os_cppr->index = 0;
15862306a36Sopenharmony_ci	icp_ops->set_priority(0);
15962306a36Sopenharmony_ci	icp_ops->teardown_cpu();
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cinoinstr void xics_kexec_teardown_cpu(int secondary)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	xics_teardown_cpu();
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	icp_ops->flush_ipi();
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Some machines need to have at least one cpu in the GIQ,
17062306a36Sopenharmony_ci	 * so leave the master cpu in the group.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	if (secondary)
17362306a36Sopenharmony_ci		xics_set_cpu_giq(xics_default_distrib_server, 0);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/* Interrupts are disabled. */
18062306a36Sopenharmony_civoid xics_migrate_irqs_away(void)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
18362306a36Sopenharmony_ci	unsigned int irq, virq;
18462306a36Sopenharmony_ci	struct irq_desc *desc;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	pr_debug("%s: CPU %u\n", __func__, cpu);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* If we used to be the default server, move to the new "boot_cpuid" */
18962306a36Sopenharmony_ci	if (hw_cpu == xics_default_server)
19062306a36Sopenharmony_ci		xics_update_irq_servers();
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Reject any interrupt that was queued to us... */
19362306a36Sopenharmony_ci	icp_ops->set_priority(0);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Remove ourselves from the global interrupt queue */
19662306a36Sopenharmony_ci	xics_set_cpu_giq(xics_default_distrib_server, 0);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	for_each_irq_desc(virq, desc) {
19962306a36Sopenharmony_ci		struct irq_chip *chip;
20062306a36Sopenharmony_ci		long server;
20162306a36Sopenharmony_ci		unsigned long flags;
20262306a36Sopenharmony_ci		struct irq_data *irqd;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		/* We can't set affinity on ISA interrupts */
20562306a36Sopenharmony_ci		if (virq < NR_IRQS_LEGACY)
20662306a36Sopenharmony_ci			continue;
20762306a36Sopenharmony_ci		/* We only need to migrate enabled IRQS */
20862306a36Sopenharmony_ci		if (!desc->action)
20962306a36Sopenharmony_ci			continue;
21062306a36Sopenharmony_ci		/* We need a mapping in the XICS IRQ domain */
21162306a36Sopenharmony_ci		irqd = irq_domain_get_irq_data(xics_host, virq);
21262306a36Sopenharmony_ci		if (!irqd)
21362306a36Sopenharmony_ci			continue;
21462306a36Sopenharmony_ci		irq = irqd_to_hwirq(irqd);
21562306a36Sopenharmony_ci		/* We need to get IPIs still. */
21662306a36Sopenharmony_ci		if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
21762306a36Sopenharmony_ci			continue;
21862306a36Sopenharmony_ci		chip = irq_desc_get_chip(desc);
21962306a36Sopenharmony_ci		if (!chip || !chip->irq_set_affinity)
22062306a36Sopenharmony_ci			continue;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		raw_spin_lock_irqsave(&desc->lock, flags);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		/* Locate interrupt server */
22562306a36Sopenharmony_ci		server = xics_ics->get_server(xics_ics, irq);
22662306a36Sopenharmony_ci		if (server < 0) {
22762306a36Sopenharmony_ci			pr_err("%s: Can't find server for irq %d/%x\n",
22862306a36Sopenharmony_ci			       __func__, virq, irq);
22962306a36Sopenharmony_ci			goto unlock;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		/* We only support delivery to all cpus or to one cpu.
23362306a36Sopenharmony_ci		 * The irq has to be migrated only in the single cpu
23462306a36Sopenharmony_ci		 * case.
23562306a36Sopenharmony_ci		 */
23662306a36Sopenharmony_ci		if (server != hw_cpu)
23762306a36Sopenharmony_ci			goto unlock;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		/* This is expected during cpu offline. */
24062306a36Sopenharmony_ci		if (cpu_online(cpu))
24162306a36Sopenharmony_ci			pr_warn("IRQ %u affinity broken off cpu %u\n",
24262306a36Sopenharmony_ci				virq, cpu);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		/* Reset affinity to all cpus */
24562306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&desc->lock, flags);
24662306a36Sopenharmony_ci		irq_set_affinity(virq, cpu_all_mask);
24762306a36Sopenharmony_ci		continue;
24862306a36Sopenharmony_ciunlock:
24962306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&desc->lock, flags);
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Allow "sufficient" time to drop any inflight IRQ's */
25362306a36Sopenharmony_ci	mdelay(5);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/*
25662306a36Sopenharmony_ci	 * Allow IPIs again. This is done at the very end, after migrating all
25762306a36Sopenharmony_ci	 * interrupts, the expectation is that we'll only get woken up by an IPI
25862306a36Sopenharmony_ci	 * interrupt beyond this point, but leave externals masked just to be
25962306a36Sopenharmony_ci	 * safe. If we're using icp-opal this may actually allow all
26062306a36Sopenharmony_ci	 * interrupts anyway, but that should be OK.
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	icp_ops->set_priority(DEFAULT_PRIORITY);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci#ifdef CONFIG_SMP
26862306a36Sopenharmony_ci/*
26962306a36Sopenharmony_ci * For the moment we only implement delivery to all cpus or one cpu.
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * If the requested affinity is cpu_all_mask, we set global affinity.
27262306a36Sopenharmony_ci * If not we set it to the first cpu in the mask, even if multiple cpus
27362306a36Sopenharmony_ci * are set. This is so things like irqbalance (which set core and package
27462306a36Sopenharmony_ci * wide affinities) do the right thing.
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * We need to fix this to implement support for the links
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_ciint xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
27962306a36Sopenharmony_ci			unsigned int strict_check)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!distribute_irqs)
28362306a36Sopenharmony_ci		return xics_default_server;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!cpumask_subset(cpu_possible_mask, cpumask)) {
28662306a36Sopenharmony_ci		int server = cpumask_first_and(cpu_online_mask, cpumask);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		if (server < nr_cpu_ids)
28962306a36Sopenharmony_ci			return get_hard_smp_processor_id(server);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		if (strict_check)
29262306a36Sopenharmony_ci			return -1;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Workaround issue with some versions of JS20 firmware that
29762306a36Sopenharmony_ci	 * deliver interrupts to cpus which haven't been started. This
29862306a36Sopenharmony_ci	 * happens when using the maxcpus= boot option.
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	if (cpumask_equal(cpu_online_mask, cpu_present_mask))
30162306a36Sopenharmony_ci		return xics_default_distrib_server;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return xics_default_server;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci#endif /* CONFIG_SMP */
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int xics_host_match(struct irq_domain *h, struct device_node *node,
30862306a36Sopenharmony_ci			   enum irq_domain_bus_token bus_token)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (WARN_ON(!xics_ics))
31162306a36Sopenharmony_ci		return 0;
31262306a36Sopenharmony_ci	return xics_ics->host_match(xics_ics, node) ? 1 : 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/* Dummies */
31662306a36Sopenharmony_cistatic void xics_ipi_unmask(struct irq_data *d) { }
31762306a36Sopenharmony_cistatic void xics_ipi_mask(struct irq_data *d) { }
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct irq_chip xics_ipi_chip = {
32062306a36Sopenharmony_ci	.name = "XICS",
32162306a36Sopenharmony_ci	.irq_eoi = NULL, /* Patched at init time */
32262306a36Sopenharmony_ci	.irq_mask = xics_ipi_mask,
32362306a36Sopenharmony_ci	.irq_unmask = xics_ipi_unmask,
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int xics_host_map(struct irq_domain *domain, unsigned int virq,
32762306a36Sopenharmony_ci			 irq_hw_number_t hwirq)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hwirq);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/*
33262306a36Sopenharmony_ci	 * Mark interrupts as edge sensitive by default so that resend
33362306a36Sopenharmony_ci	 * actually works. The device-tree parsing will turn the LSIs
33462306a36Sopenharmony_ci	 * back to level.
33562306a36Sopenharmony_ci	 */
33662306a36Sopenharmony_ci	irq_clear_status_flags(virq, IRQ_LEVEL);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* Don't call into ICS for IPIs */
33962306a36Sopenharmony_ci	if (hwirq == XICS_IPI) {
34062306a36Sopenharmony_ci		irq_set_chip_and_handler(virq, &xics_ipi_chip,
34162306a36Sopenharmony_ci					 handle_percpu_irq);
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (WARN_ON(!xics_ics))
34662306a36Sopenharmony_ci		return -EINVAL;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (xics_ics->check(xics_ics, hwirq))
34962306a36Sopenharmony_ci		return -EINVAL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Let the ICS be the chip data for the XICS domain. For ICS native */
35262306a36Sopenharmony_ci	irq_domain_set_info(domain, virq, hwirq, xics_ics->chip,
35362306a36Sopenharmony_ci			    xics_ics, handle_fasteoi_irq, NULL, NULL);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int xics_host_xlate(struct irq_domain *h, struct device_node *ct,
35962306a36Sopenharmony_ci			   const u32 *intspec, unsigned int intsize,
36062306a36Sopenharmony_ci			   irq_hw_number_t *out_hwirq, unsigned int *out_flags)
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	*out_hwirq = intspec[0];
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/*
36662306a36Sopenharmony_ci	 * If intsize is at least 2, we look for the type in the second cell,
36762306a36Sopenharmony_ci	 * we assume the LSB indicates a level interrupt.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	if (intsize > 1) {
37062306a36Sopenharmony_ci		if (intspec[1] & 1)
37162306a36Sopenharmony_ci			*out_flags = IRQ_TYPE_LEVEL_LOW;
37262306a36Sopenharmony_ci		else
37362306a36Sopenharmony_ci			*out_flags = IRQ_TYPE_EDGE_RISING;
37462306a36Sopenharmony_ci	} else
37562306a36Sopenharmony_ci		*out_flags = IRQ_TYPE_LEVEL_LOW;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ciint xics_set_irq_type(struct irq_data *d, unsigned int flow_type)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	/*
38362306a36Sopenharmony_ci	 * We only support these. This has really no effect other than setting
38462306a36Sopenharmony_ci	 * the corresponding descriptor bits mind you but those will in turn
38562306a36Sopenharmony_ci	 * affect the resend function when re-enabling an edge interrupt.
38662306a36Sopenharmony_ci	 *
38762306a36Sopenharmony_ci	 * Set set the default to edge as explained in map().
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci	if (flow_type == IRQ_TYPE_DEFAULT || flow_type == IRQ_TYPE_NONE)
39062306a36Sopenharmony_ci		flow_type = IRQ_TYPE_EDGE_RISING;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (flow_type != IRQ_TYPE_EDGE_RISING &&
39362306a36Sopenharmony_ci	    flow_type != IRQ_TYPE_LEVEL_LOW)
39462306a36Sopenharmony_ci		return -EINVAL;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	irqd_set_trigger_type(d, flow_type);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return IRQ_SET_MASK_OK_NOCOPY;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciint xics_retrigger(struct irq_data *data)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	/*
40462306a36Sopenharmony_ci	 * We need to push a dummy CPPR when retriggering, since the subsequent
40562306a36Sopenharmony_ci	 * EOI will try to pop it. Passing 0 works, as the function hard codes
40662306a36Sopenharmony_ci	 * the priority value anyway.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	xics_push_cppr(0);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Tell the core to do a soft retrigger */
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
41562306a36Sopenharmony_cistatic int xics_host_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
41662306a36Sopenharmony_ci				      unsigned long *hwirq, unsigned int *type)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	return xics_host_xlate(d, to_of_node(fwspec->fwnode), fwspec->param,
41962306a36Sopenharmony_ci			       fwspec->param_count, hwirq, type);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int xics_host_domain_alloc(struct irq_domain *domain, unsigned int virq,
42362306a36Sopenharmony_ci				  unsigned int nr_irqs, void *arg)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct irq_fwspec *fwspec = arg;
42662306a36Sopenharmony_ci	irq_hw_number_t hwirq;
42762306a36Sopenharmony_ci	unsigned int type = IRQ_TYPE_NONE;
42862306a36Sopenharmony_ci	int i, rc;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	rc = xics_host_domain_translate(domain, fwspec, &hwirq, &type);
43162306a36Sopenharmony_ci	if (rc)
43262306a36Sopenharmony_ci		return rc;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	pr_debug("%s %d/%lx #%d\n", __func__, virq, hwirq, nr_irqs);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
43762306a36Sopenharmony_ci		irq_domain_set_info(domain, virq + i, hwirq + i, xics_ics->chip,
43862306a36Sopenharmony_ci				    xics_ics, handle_fasteoi_irq, NULL, NULL);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void xics_host_domain_free(struct irq_domain *domain,
44462306a36Sopenharmony_ci				  unsigned int virq, unsigned int nr_irqs)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	pr_debug("%s %d #%d\n", __func__, virq, nr_irqs);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci#endif
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const struct irq_domain_ops xics_host_ops = {
45162306a36Sopenharmony_ci#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
45262306a36Sopenharmony_ci	.alloc	= xics_host_domain_alloc,
45362306a36Sopenharmony_ci	.free	= xics_host_domain_free,
45462306a36Sopenharmony_ci	.translate = xics_host_domain_translate,
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci	.match = xics_host_match,
45762306a36Sopenharmony_ci	.map = xics_host_map,
45862306a36Sopenharmony_ci	.xlate = xics_host_xlate,
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int __init xics_allocate_domain(void)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct fwnode_handle *fn;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	fn = irq_domain_alloc_named_fwnode("XICS");
46662306a36Sopenharmony_ci	if (!fn)
46762306a36Sopenharmony_ci		return -ENOMEM;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	xics_host = irq_domain_create_tree(fn, &xics_host_ops, NULL);
47062306a36Sopenharmony_ci	if (!xics_host) {
47162306a36Sopenharmony_ci		irq_domain_free_fwnode(fn);
47262306a36Sopenharmony_ci		return -ENOMEM;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	irq_set_default_host(xics_host);
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_civoid __init xics_register_ics(struct ics *ics)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	if (WARN_ONCE(xics_ics, "XICS: Source Controller is already defined !"))
48262306a36Sopenharmony_ci		return;
48362306a36Sopenharmony_ci	xics_ics = ics;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void __init xics_get_server_size(void)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct device_node *np;
48962306a36Sopenharmony_ci	const __be32 *isize;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* We fetch the interrupt server size from the first ICS node
49262306a36Sopenharmony_ci	 * we find if any
49362306a36Sopenharmony_ci	 */
49462306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ibm,ppc-xics");
49562306a36Sopenharmony_ci	if (!np)
49662306a36Sopenharmony_ci		return;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
49962306a36Sopenharmony_ci	if (isize)
50062306a36Sopenharmony_ci		xics_interrupt_server_size = be32_to_cpu(*isize);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	of_node_put(np);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_civoid __init xics_init(void)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	int rc = -1;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Fist locate ICP */
51062306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_LPAR))
51162306a36Sopenharmony_ci		rc = icp_hv_init();
51262306a36Sopenharmony_ci	if (rc < 0) {
51362306a36Sopenharmony_ci		rc = icp_native_init();
51462306a36Sopenharmony_ci		if (rc == -ENODEV)
51562306a36Sopenharmony_ci		    rc = icp_opal_init();
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci	if (rc < 0) {
51862306a36Sopenharmony_ci		pr_warn("XICS: Cannot find a Presentation Controller !\n");
51962306a36Sopenharmony_ci		return;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* Copy get_irq callback over to ppc_md */
52362306a36Sopenharmony_ci	ppc_md.get_irq = icp_ops->get_irq;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Patch up IPI chip EOI */
52662306a36Sopenharmony_ci	xics_ipi_chip.irq_eoi = icp_ops->eoi;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* Now locate ICS */
52962306a36Sopenharmony_ci	rc = ics_rtas_init();
53062306a36Sopenharmony_ci	if (rc < 0)
53162306a36Sopenharmony_ci		rc = ics_opal_init();
53262306a36Sopenharmony_ci	if (rc < 0)
53362306a36Sopenharmony_ci		rc = ics_native_init();
53462306a36Sopenharmony_ci	if (rc < 0)
53562306a36Sopenharmony_ci		pr_warn("XICS: Cannot find a Source Controller !\n");
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Initialize common bits */
53862306a36Sopenharmony_ci	xics_get_server_size();
53962306a36Sopenharmony_ci	xics_update_irq_servers();
54062306a36Sopenharmony_ci	rc = xics_allocate_domain();
54162306a36Sopenharmony_ci	if (rc < 0)
54262306a36Sopenharmony_ci		pr_err("XICS: Failed to create IRQ domain");
54362306a36Sopenharmony_ci	xics_setup_cpu();
54462306a36Sopenharmony_ci}
545