162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/powerpc/sysdev/uic.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * IBM PowerPC 4xx Universal Interrupt Controller
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/reboot.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/stddef.h>
1562306a36Sopenharmony_ci#include <linux/sched.h>
1662306a36Sopenharmony_ci#include <linux/signal.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/irq.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/kernel_stat.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/of_irq.h>
2462306a36Sopenharmony_ci#include <asm/irq.h>
2562306a36Sopenharmony_ci#include <asm/io.h>
2662306a36Sopenharmony_ci#include <asm/dcr.h>
2762306a36Sopenharmony_ci#include <asm/uic.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define NR_UIC_INTS	32
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define UIC_SR		0x0
3262306a36Sopenharmony_ci#define UIC_ER		0x2
3362306a36Sopenharmony_ci#define UIC_CR		0x3
3462306a36Sopenharmony_ci#define UIC_PR		0x4
3562306a36Sopenharmony_ci#define UIC_TR		0x5
3662306a36Sopenharmony_ci#define UIC_MSR		0x6
3762306a36Sopenharmony_ci#define UIC_VR		0x7
3862306a36Sopenharmony_ci#define UIC_VCR		0x8
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct uic *primary_uic;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct uic {
4362306a36Sopenharmony_ci	int index;
4462306a36Sopenharmony_ci	int dcrbase;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	raw_spinlock_t lock;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* The remapper for this UIC */
4962306a36Sopenharmony_ci	struct irq_domain	*irqhost;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic void uic_unmask_irq(struct irq_data *d)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct uic *uic = irq_data_get_irq_chip_data(d);
5562306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
5662306a36Sopenharmony_ci	unsigned long flags;
5762306a36Sopenharmony_ci	u32 er, sr;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	sr = 1 << (31-src);
6062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&uic->lock, flags);
6162306a36Sopenharmony_ci	/* ack level-triggered interrupts here */
6262306a36Sopenharmony_ci	if (irqd_is_level_type(d))
6362306a36Sopenharmony_ci		mtdcr(uic->dcrbase + UIC_SR, sr);
6462306a36Sopenharmony_ci	er = mfdcr(uic->dcrbase + UIC_ER);
6562306a36Sopenharmony_ci	er |= sr;
6662306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_ER, er);
6762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&uic->lock, flags);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void uic_mask_irq(struct irq_data *d)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct uic *uic = irq_data_get_irq_chip_data(d);
7362306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
7462306a36Sopenharmony_ci	unsigned long flags;
7562306a36Sopenharmony_ci	u32 er;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&uic->lock, flags);
7862306a36Sopenharmony_ci	er = mfdcr(uic->dcrbase + UIC_ER);
7962306a36Sopenharmony_ci	er &= ~(1 << (31 - src));
8062306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_ER, er);
8162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&uic->lock, flags);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void uic_ack_irq(struct irq_data *d)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct uic *uic = irq_data_get_irq_chip_data(d);
8762306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
8862306a36Sopenharmony_ci	unsigned long flags;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&uic->lock, flags);
9162306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src));
9262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&uic->lock, flags);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void uic_mask_ack_irq(struct irq_data *d)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct uic *uic = irq_data_get_irq_chip_data(d);
9862306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
9962306a36Sopenharmony_ci	unsigned long flags;
10062306a36Sopenharmony_ci	u32 er, sr;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	sr = 1 << (31-src);
10362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&uic->lock, flags);
10462306a36Sopenharmony_ci	er = mfdcr(uic->dcrbase + UIC_ER);
10562306a36Sopenharmony_ci	er &= ~sr;
10662306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_ER, er);
10762306a36Sopenharmony_ci 	/* On the UIC, acking (i.e. clearing the SR bit)
10862306a36Sopenharmony_ci	 * a level irq will have no effect if the interrupt
10962306a36Sopenharmony_ci	 * is still asserted by the device, even if
11062306a36Sopenharmony_ci	 * the interrupt is already masked. Therefore
11162306a36Sopenharmony_ci	 * we only ack the egde interrupts here, while
11262306a36Sopenharmony_ci	 * level interrupts are ack'ed after the actual
11362306a36Sopenharmony_ci	 * isr call in the uic_unmask_irq()
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	if (!irqd_is_level_type(d))
11662306a36Sopenharmony_ci		mtdcr(uic->dcrbase + UIC_SR, sr);
11762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&uic->lock, flags);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int uic_set_irq_type(struct irq_data *d, unsigned int flow_type)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct uic *uic = irq_data_get_irq_chip_data(d);
12362306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
12462306a36Sopenharmony_ci	unsigned long flags;
12562306a36Sopenharmony_ci	int trigger, polarity;
12662306a36Sopenharmony_ci	u32 tr, pr, mask;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	switch (flow_type & IRQ_TYPE_SENSE_MASK) {
12962306a36Sopenharmony_ci	case IRQ_TYPE_NONE:
13062306a36Sopenharmony_ci		uic_mask_irq(d);
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
13462306a36Sopenharmony_ci		trigger = 1; polarity = 1;
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
13762306a36Sopenharmony_ci		trigger = 1; polarity = 0;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
14062306a36Sopenharmony_ci		trigger = 0; polarity = 1;
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
14362306a36Sopenharmony_ci		trigger = 0; polarity = 0;
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	default:
14662306a36Sopenharmony_ci		return -EINVAL;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	mask = ~(1 << (31 - src));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&uic->lock, flags);
15262306a36Sopenharmony_ci	tr = mfdcr(uic->dcrbase + UIC_TR);
15362306a36Sopenharmony_ci	pr = mfdcr(uic->dcrbase + UIC_PR);
15462306a36Sopenharmony_ci	tr = (tr & mask) | (trigger << (31-src));
15562306a36Sopenharmony_ci	pr = (pr & mask) | (polarity << (31-src));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_PR, pr);
15862306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_TR, tr);
15962306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_SR, ~mask);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&uic->lock, flags);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic struct irq_chip uic_irq_chip = {
16762306a36Sopenharmony_ci	.name		= "UIC",
16862306a36Sopenharmony_ci	.irq_unmask	= uic_unmask_irq,
16962306a36Sopenharmony_ci	.irq_mask	= uic_mask_irq,
17062306a36Sopenharmony_ci	.irq_mask_ack	= uic_mask_ack_irq,
17162306a36Sopenharmony_ci	.irq_ack	= uic_ack_irq,
17262306a36Sopenharmony_ci	.irq_set_type	= uic_set_irq_type,
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int uic_host_map(struct irq_domain *h, unsigned int virq,
17662306a36Sopenharmony_ci			irq_hw_number_t hw)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct uic *uic = h->host_data;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	irq_set_chip_data(virq, uic);
18162306a36Sopenharmony_ci	/* Despite the name, handle_level_irq() works for both level
18262306a36Sopenharmony_ci	 * and edge irqs on UIC.  FIXME: check this is correct */
18362306a36Sopenharmony_ci	irq_set_chip_and_handler(virq, &uic_irq_chip, handle_level_irq);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* Set default irq type */
18662306a36Sopenharmony_ci	irq_set_irq_type(virq, IRQ_TYPE_NONE);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic const struct irq_domain_ops uic_host_ops = {
19262306a36Sopenharmony_ci	.map	= uic_host_map,
19362306a36Sopenharmony_ci	.xlate	= irq_domain_xlate_twocell,
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void uic_irq_cascade(struct irq_desc *desc)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
19962306a36Sopenharmony_ci	struct irq_data *idata = irq_desc_get_irq_data(desc);
20062306a36Sopenharmony_ci	struct uic *uic = irq_desc_get_handler_data(desc);
20162306a36Sopenharmony_ci	u32 msr;
20262306a36Sopenharmony_ci	int src;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	raw_spin_lock(&desc->lock);
20562306a36Sopenharmony_ci	if (irqd_is_level_type(idata))
20662306a36Sopenharmony_ci		chip->irq_mask(idata);
20762306a36Sopenharmony_ci	else
20862306a36Sopenharmony_ci		chip->irq_mask_ack(idata);
20962306a36Sopenharmony_ci	raw_spin_unlock(&desc->lock);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	msr = mfdcr(uic->dcrbase + UIC_MSR);
21262306a36Sopenharmony_ci	if (!msr) /* spurious interrupt */
21362306a36Sopenharmony_ci		goto uic_irq_ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	src = 32 - ffs(msr);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	generic_handle_domain_irq(uic->irqhost, src);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ciuic_irq_ret:
22062306a36Sopenharmony_ci	raw_spin_lock(&desc->lock);
22162306a36Sopenharmony_ci	if (irqd_is_level_type(idata))
22262306a36Sopenharmony_ci		chip->irq_ack(idata);
22362306a36Sopenharmony_ci	if (!irqd_irq_disabled(idata) && chip->irq_unmask)
22462306a36Sopenharmony_ci		chip->irq_unmask(idata);
22562306a36Sopenharmony_ci	raw_spin_unlock(&desc->lock);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic struct uic * __init uic_init_one(struct device_node *node)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct uic *uic;
23162306a36Sopenharmony_ci	const u32 *indexp, *dcrreg;
23262306a36Sopenharmony_ci	int len;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	BUG_ON(! of_device_is_compatible(node, "ibm,uic"));
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	uic = kzalloc(sizeof(*uic), GFP_KERNEL);
23762306a36Sopenharmony_ci	if (! uic)
23862306a36Sopenharmony_ci		return NULL; /* FIXME: panic? */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	raw_spin_lock_init(&uic->lock);
24162306a36Sopenharmony_ci	indexp = of_get_property(node, "cell-index", &len);
24262306a36Sopenharmony_ci	if (!indexp || (len != sizeof(u32))) {
24362306a36Sopenharmony_ci		printk(KERN_ERR "uic: Device node %pOF has missing or invalid "
24462306a36Sopenharmony_ci		       "cell-index property\n", node);
24562306a36Sopenharmony_ci		return NULL;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	uic->index = *indexp;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	dcrreg = of_get_property(node, "dcr-reg", &len);
25062306a36Sopenharmony_ci	if (!dcrreg || (len != 2*sizeof(u32))) {
25162306a36Sopenharmony_ci		printk(KERN_ERR "uic: Device node %pOF has missing or invalid "
25262306a36Sopenharmony_ci		       "dcr-reg property\n", node);
25362306a36Sopenharmony_ci		return NULL;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	uic->dcrbase = *dcrreg;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	uic->irqhost = irq_domain_add_linear(node, NR_UIC_INTS, &uic_host_ops,
25862306a36Sopenharmony_ci					     uic);
25962306a36Sopenharmony_ci	if (! uic->irqhost)
26062306a36Sopenharmony_ci		return NULL; /* FIXME: panic? */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Start with all interrupts disabled, level and non-critical */
26362306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_ER, 0);
26462306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_CR, 0);
26562306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_TR, 0);
26662306a36Sopenharmony_ci	/* Clear any pending interrupts, in case the firmware left some */
26762306a36Sopenharmony_ci	mtdcr(uic->dcrbase + UIC_SR, 0xffffffff);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index,
27062306a36Sopenharmony_ci		NR_UIC_INTS, uic->dcrbase);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return uic;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_civoid __init uic_init_tree(void)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct device_node *np;
27862306a36Sopenharmony_ci	struct uic *uic;
27962306a36Sopenharmony_ci	const u32 *interrupts;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* First locate and initialize the top-level UIC */
28262306a36Sopenharmony_ci	for_each_compatible_node(np, NULL, "ibm,uic") {
28362306a36Sopenharmony_ci		interrupts = of_get_property(np, "interrupts", NULL);
28462306a36Sopenharmony_ci		if (!interrupts)
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the
28962306a36Sopenharmony_ci		      * top-level interrupt controller */
29062306a36Sopenharmony_ci	primary_uic = uic_init_one(np);
29162306a36Sopenharmony_ci	if (!primary_uic)
29262306a36Sopenharmony_ci		panic("Unable to initialize primary UIC %pOF\n", np);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	irq_set_default_host(primary_uic->irqhost);
29562306a36Sopenharmony_ci	of_node_put(np);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* The scan again for cascaded UICs */
29862306a36Sopenharmony_ci	for_each_compatible_node(np, NULL, "ibm,uic") {
29962306a36Sopenharmony_ci		interrupts = of_get_property(np, "interrupts", NULL);
30062306a36Sopenharmony_ci		if (interrupts) {
30162306a36Sopenharmony_ci			/* Secondary UIC */
30262306a36Sopenharmony_ci			int cascade_virq;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci			uic = uic_init_one(np);
30562306a36Sopenharmony_ci			if (! uic)
30662306a36Sopenharmony_ci				panic("Unable to initialize a secondary UIC %pOF\n",
30762306a36Sopenharmony_ci				      np);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci			cascade_virq = irq_of_parse_and_map(np, 0);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci			irq_set_handler_data(cascade_virq, uic);
31262306a36Sopenharmony_ci			irq_set_chained_handler(cascade_virq, uic_irq_cascade);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			/* FIXME: setup critical cascade?? */
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/* Return an interrupt vector or 0 if no interrupt is pending. */
32062306a36Sopenharmony_ciunsigned int uic_get_irq(void)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	u32 msr;
32362306a36Sopenharmony_ci	int src;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	BUG_ON(! primary_uic);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	msr = mfdcr(primary_uic->dcrbase + UIC_MSR);
32862306a36Sopenharmony_ci	src = 32 - ffs(msr);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return irq_linear_revmap(primary_uic->irqhost, src);
33162306a36Sopenharmony_ci}
332