162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/powerpc/sysdev/qe_lib/qe_ic.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 Freescale Semiconductor, Inc.  All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Li Yang <leoli@freescale.com>
862306a36Sopenharmony_ci * Based on code from Shlomi Gridish <gridish@freescale.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * QUICC ENGINE Interrupt Controller
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/of_irq.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/irq.h>
1962306a36Sopenharmony_ci#include <linux/reboot.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/stddef.h>
2262306a36Sopenharmony_ci#include <linux/sched.h>
2362306a36Sopenharmony_ci#include <linux/signal.h>
2462306a36Sopenharmony_ci#include <linux/device.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci#include <linux/platform_device.h>
2762306a36Sopenharmony_ci#include <asm/irq.h>
2862306a36Sopenharmony_ci#include <asm/io.h>
2962306a36Sopenharmony_ci#include <soc/fsl/qe/qe.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define NR_QE_IC_INTS		64
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* QE IC registers offset */
3462306a36Sopenharmony_ci#define QEIC_CICR		0x00
3562306a36Sopenharmony_ci#define QEIC_CIVEC		0x04
3662306a36Sopenharmony_ci#define QEIC_CIPXCC		0x10
3762306a36Sopenharmony_ci#define QEIC_CIPYCC		0x14
3862306a36Sopenharmony_ci#define QEIC_CIPWCC		0x18
3962306a36Sopenharmony_ci#define QEIC_CIPZCC		0x1c
4062306a36Sopenharmony_ci#define QEIC_CIMR		0x20
4162306a36Sopenharmony_ci#define QEIC_CRIMR		0x24
4262306a36Sopenharmony_ci#define QEIC_CIPRTA		0x30
4362306a36Sopenharmony_ci#define QEIC_CIPRTB		0x34
4462306a36Sopenharmony_ci#define QEIC_CHIVEC		0x60
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct qe_ic {
4762306a36Sopenharmony_ci	/* Control registers offset */
4862306a36Sopenharmony_ci	__be32 __iomem *regs;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* The remapper for this QEIC */
5162306a36Sopenharmony_ci	struct irq_domain *irqhost;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* The "linux" controller struct */
5462306a36Sopenharmony_ci	struct irq_chip hc_irq;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* VIRQ numbers of QE high/low irqs */
5762306a36Sopenharmony_ci	int virq_high;
5862306a36Sopenharmony_ci	int virq_low;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * QE interrupt controller internal structure
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistruct qe_ic_info {
6562306a36Sopenharmony_ci	/* Location of this source at the QIMR register */
6662306a36Sopenharmony_ci	u32	mask;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Mask register offset */
6962306a36Sopenharmony_ci	u32	mask_reg;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/*
7262306a36Sopenharmony_ci	 * For grouped interrupts sources - the interrupt code as
7362306a36Sopenharmony_ci	 * appears at the group priority register
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	u8	pri_code;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Group priority register offset */
7862306a36Sopenharmony_ci	u32	pri_reg;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(qe_ic_lock);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct qe_ic_info qe_ic_info[] = {
8462306a36Sopenharmony_ci	[1] = {
8562306a36Sopenharmony_ci	       .mask = 0x00008000,
8662306a36Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
8762306a36Sopenharmony_ci	       .pri_code = 0,
8862306a36Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
8962306a36Sopenharmony_ci	       },
9062306a36Sopenharmony_ci	[2] = {
9162306a36Sopenharmony_ci	       .mask = 0x00004000,
9262306a36Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
9362306a36Sopenharmony_ci	       .pri_code = 1,
9462306a36Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
9562306a36Sopenharmony_ci	       },
9662306a36Sopenharmony_ci	[3] = {
9762306a36Sopenharmony_ci	       .mask = 0x00002000,
9862306a36Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
9962306a36Sopenharmony_ci	       .pri_code = 2,
10062306a36Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
10162306a36Sopenharmony_ci	       },
10262306a36Sopenharmony_ci	[10] = {
10362306a36Sopenharmony_ci		.mask = 0x00000040,
10462306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
10562306a36Sopenharmony_ci		.pri_code = 1,
10662306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
10762306a36Sopenharmony_ci		},
10862306a36Sopenharmony_ci	[11] = {
10962306a36Sopenharmony_ci		.mask = 0x00000020,
11062306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
11162306a36Sopenharmony_ci		.pri_code = 2,
11262306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
11362306a36Sopenharmony_ci		},
11462306a36Sopenharmony_ci	[12] = {
11562306a36Sopenharmony_ci		.mask = 0x00000010,
11662306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
11762306a36Sopenharmony_ci		.pri_code = 3,
11862306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
11962306a36Sopenharmony_ci		},
12062306a36Sopenharmony_ci	[13] = {
12162306a36Sopenharmony_ci		.mask = 0x00000008,
12262306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
12362306a36Sopenharmony_ci		.pri_code = 4,
12462306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
12562306a36Sopenharmony_ci		},
12662306a36Sopenharmony_ci	[14] = {
12762306a36Sopenharmony_ci		.mask = 0x00000004,
12862306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
12962306a36Sopenharmony_ci		.pri_code = 5,
13062306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
13162306a36Sopenharmony_ci		},
13262306a36Sopenharmony_ci	[15] = {
13362306a36Sopenharmony_ci		.mask = 0x00000002,
13462306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
13562306a36Sopenharmony_ci		.pri_code = 6,
13662306a36Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
13762306a36Sopenharmony_ci		},
13862306a36Sopenharmony_ci	[20] = {
13962306a36Sopenharmony_ci		.mask = 0x10000000,
14062306a36Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
14162306a36Sopenharmony_ci		.pri_code = 3,
14262306a36Sopenharmony_ci		.pri_reg = QEIC_CIPRTA,
14362306a36Sopenharmony_ci		},
14462306a36Sopenharmony_ci	[25] = {
14562306a36Sopenharmony_ci		.mask = 0x00800000,
14662306a36Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
14762306a36Sopenharmony_ci		.pri_code = 0,
14862306a36Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
14962306a36Sopenharmony_ci		},
15062306a36Sopenharmony_ci	[26] = {
15162306a36Sopenharmony_ci		.mask = 0x00400000,
15262306a36Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
15362306a36Sopenharmony_ci		.pri_code = 1,
15462306a36Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
15562306a36Sopenharmony_ci		},
15662306a36Sopenharmony_ci	[27] = {
15762306a36Sopenharmony_ci		.mask = 0x00200000,
15862306a36Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
15962306a36Sopenharmony_ci		.pri_code = 2,
16062306a36Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
16162306a36Sopenharmony_ci		},
16262306a36Sopenharmony_ci	[28] = {
16362306a36Sopenharmony_ci		.mask = 0x00100000,
16462306a36Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
16562306a36Sopenharmony_ci		.pri_code = 3,
16662306a36Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
16762306a36Sopenharmony_ci		},
16862306a36Sopenharmony_ci	[32] = {
16962306a36Sopenharmony_ci		.mask = 0x80000000,
17062306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
17162306a36Sopenharmony_ci		.pri_code = 0,
17262306a36Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
17362306a36Sopenharmony_ci		},
17462306a36Sopenharmony_ci	[33] = {
17562306a36Sopenharmony_ci		.mask = 0x40000000,
17662306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
17762306a36Sopenharmony_ci		.pri_code = 1,
17862306a36Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
17962306a36Sopenharmony_ci		},
18062306a36Sopenharmony_ci	[34] = {
18162306a36Sopenharmony_ci		.mask = 0x20000000,
18262306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
18362306a36Sopenharmony_ci		.pri_code = 2,
18462306a36Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
18562306a36Sopenharmony_ci		},
18662306a36Sopenharmony_ci	[35] = {
18762306a36Sopenharmony_ci		.mask = 0x10000000,
18862306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
18962306a36Sopenharmony_ci		.pri_code = 3,
19062306a36Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
19162306a36Sopenharmony_ci		},
19262306a36Sopenharmony_ci	[36] = {
19362306a36Sopenharmony_ci		.mask = 0x08000000,
19462306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
19562306a36Sopenharmony_ci		.pri_code = 4,
19662306a36Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
19762306a36Sopenharmony_ci		},
19862306a36Sopenharmony_ci	[40] = {
19962306a36Sopenharmony_ci		.mask = 0x00800000,
20062306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
20162306a36Sopenharmony_ci		.pri_code = 0,
20262306a36Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
20362306a36Sopenharmony_ci		},
20462306a36Sopenharmony_ci	[41] = {
20562306a36Sopenharmony_ci		.mask = 0x00400000,
20662306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
20762306a36Sopenharmony_ci		.pri_code = 1,
20862306a36Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
20962306a36Sopenharmony_ci		},
21062306a36Sopenharmony_ci	[42] = {
21162306a36Sopenharmony_ci		.mask = 0x00200000,
21262306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
21362306a36Sopenharmony_ci		.pri_code = 2,
21462306a36Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
21562306a36Sopenharmony_ci		},
21662306a36Sopenharmony_ci	[43] = {
21762306a36Sopenharmony_ci		.mask = 0x00100000,
21862306a36Sopenharmony_ci		.mask_reg = QEIC_CIMR,
21962306a36Sopenharmony_ci		.pri_code = 3,
22062306a36Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
22162306a36Sopenharmony_ci		},
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic inline u32 qe_ic_read(__be32  __iomem *base, unsigned int reg)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	return ioread32be(base + (reg >> 2));
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic inline void qe_ic_write(__be32  __iomem *base, unsigned int reg,
23062306a36Sopenharmony_ci			       u32 value)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	iowrite32be(value, base + (reg >> 2));
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	return irq_get_chip_data(virq);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return irq_data_get_irq_chip_data(d);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void qe_ic_unmask_irq(struct irq_data *d)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
24862306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
24962306a36Sopenharmony_ci	unsigned long flags;
25062306a36Sopenharmony_ci	u32 temp;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&qe_ic_lock, flags);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
25562306a36Sopenharmony_ci	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
25662306a36Sopenharmony_ci		    temp | qe_ic_info[src].mask);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void qe_ic_mask_irq(struct irq_data *d)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
26462306a36Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
26562306a36Sopenharmony_ci	unsigned long flags;
26662306a36Sopenharmony_ci	u32 temp;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&qe_ic_lock, flags);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
27162306a36Sopenharmony_ci	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
27262306a36Sopenharmony_ci		    temp & ~qe_ic_info[src].mask);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Flush the above write before enabling interrupts; otherwise,
27562306a36Sopenharmony_ci	 * spurious interrupts will sometimes happen.  To be 100% sure
27662306a36Sopenharmony_ci	 * that the write has reached the device before interrupts are
27762306a36Sopenharmony_ci	 * enabled, the mask register would have to be read back; however,
27862306a36Sopenharmony_ci	 * this is not required for correctness, only to avoid wasting
27962306a36Sopenharmony_ci	 * time on a large number of spurious interrupts.  In testing,
28062306a36Sopenharmony_ci	 * a sync reduced the observed spurious interrupts to zero.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	mb();
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct irq_chip qe_ic_irq_chip = {
28862306a36Sopenharmony_ci	.name = "QEIC",
28962306a36Sopenharmony_ci	.irq_unmask = qe_ic_unmask_irq,
29062306a36Sopenharmony_ci	.irq_mask = qe_ic_mask_irq,
29162306a36Sopenharmony_ci	.irq_mask_ack = qe_ic_mask_irq,
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int qe_ic_host_match(struct irq_domain *h, struct device_node *node,
29562306a36Sopenharmony_ci			    enum irq_domain_bus_token bus_token)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	/* Exact match, unless qe_ic node is NULL */
29862306a36Sopenharmony_ci	struct device_node *of_node = irq_domain_get_of_node(h);
29962306a36Sopenharmony_ci	return of_node == NULL || of_node == node;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
30362306a36Sopenharmony_ci			  irq_hw_number_t hw)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct qe_ic *qe_ic = h->host_data;
30662306a36Sopenharmony_ci	struct irq_chip *chip;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (hw >= ARRAY_SIZE(qe_ic_info)) {
30962306a36Sopenharmony_ci		pr_err("%s: Invalid hw irq number for QEIC\n", __func__);
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (qe_ic_info[hw].mask == 0) {
31462306a36Sopenharmony_ci		printk(KERN_ERR "Can't map reserved IRQ\n");
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	/* Default chip */
31862306a36Sopenharmony_ci	chip = &qe_ic->hc_irq;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	irq_set_chip_data(virq, qe_ic);
32162306a36Sopenharmony_ci	irq_set_status_flags(virq, IRQ_LEVEL);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	irq_set_chip_and_handler(virq, chip, handle_level_irq);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return 0;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const struct irq_domain_ops qe_ic_host_ops = {
32962306a36Sopenharmony_ci	.match = qe_ic_host_match,
33062306a36Sopenharmony_ci	.map = qe_ic_host_map,
33162306a36Sopenharmony_ci	.xlate = irq_domain_xlate_onetwocell,
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/* Return an interrupt vector or 0 if no interrupt is pending. */
33562306a36Sopenharmony_cistatic unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int irq;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	BUG_ON(qe_ic == NULL);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* get the interrupt source vector. */
34262306a36Sopenharmony_ci	irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (irq == 0)
34562306a36Sopenharmony_ci		return 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return irq_linear_revmap(qe_ic->irqhost, irq);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/* Return an interrupt vector or 0 if no interrupt is pending. */
35162306a36Sopenharmony_cistatic unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	int irq;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	BUG_ON(qe_ic == NULL);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* get the interrupt source vector. */
35862306a36Sopenharmony_ci	irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (irq == 0)
36162306a36Sopenharmony_ci		return 0;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return irq_linear_revmap(qe_ic->irqhost, irq);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void qe_ic_cascade_low(struct irq_desc *desc)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
36962306a36Sopenharmony_ci	unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic);
37062306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (cascade_irq != 0)
37362306a36Sopenharmony_ci		generic_handle_irq(cascade_irq);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (chip->irq_eoi)
37662306a36Sopenharmony_ci		chip->irq_eoi(&desc->irq_data);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void qe_ic_cascade_high(struct irq_desc *desc)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
38262306a36Sopenharmony_ci	unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic);
38362306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (cascade_irq != 0)
38662306a36Sopenharmony_ci		generic_handle_irq(cascade_irq);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (chip->irq_eoi)
38962306a36Sopenharmony_ci		chip->irq_eoi(&desc->irq_data);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void qe_ic_cascade_muxed_mpic(struct irq_desc *desc)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
39562306a36Sopenharmony_ci	unsigned int cascade_irq;
39662306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	cascade_irq = qe_ic_get_high_irq(qe_ic);
39962306a36Sopenharmony_ci	if (cascade_irq == 0)
40062306a36Sopenharmony_ci		cascade_irq = qe_ic_get_low_irq(qe_ic);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (cascade_irq != 0)
40362306a36Sopenharmony_ci		generic_handle_irq(cascade_irq);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	chip->irq_eoi(&desc->irq_data);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int qe_ic_init(struct platform_device *pdev)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
41162306a36Sopenharmony_ci	void (*low_handler)(struct irq_desc *desc);
41262306a36Sopenharmony_ci	void (*high_handler)(struct irq_desc *desc);
41362306a36Sopenharmony_ci	struct qe_ic *qe_ic;
41462306a36Sopenharmony_ci	struct resource *res;
41562306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
41862306a36Sopenharmony_ci	if (res == NULL) {
41962306a36Sopenharmony_ci		dev_err(dev, "no memory resource defined\n");
42062306a36Sopenharmony_ci		return -ENODEV;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	qe_ic = devm_kzalloc(dev, sizeof(*qe_ic), GFP_KERNEL);
42462306a36Sopenharmony_ci	if (qe_ic == NULL)
42562306a36Sopenharmony_ci		return -ENOMEM;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	qe_ic->regs = devm_ioremap(dev, res->start, resource_size(res));
42862306a36Sopenharmony_ci	if (qe_ic->regs == NULL) {
42962306a36Sopenharmony_ci		dev_err(dev, "failed to ioremap() registers\n");
43062306a36Sopenharmony_ci		return -ENODEV;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	qe_ic->hc_irq = qe_ic_irq_chip;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	qe_ic->virq_high = platform_get_irq(pdev, 0);
43662306a36Sopenharmony_ci	qe_ic->virq_low = platform_get_irq(pdev, 1);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (qe_ic->virq_low <= 0)
43962306a36Sopenharmony_ci		return -ENODEV;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (qe_ic->virq_high > 0 && qe_ic->virq_high != qe_ic->virq_low) {
44262306a36Sopenharmony_ci		low_handler = qe_ic_cascade_low;
44362306a36Sopenharmony_ci		high_handler = qe_ic_cascade_high;
44462306a36Sopenharmony_ci	} else {
44562306a36Sopenharmony_ci		low_handler = qe_ic_cascade_muxed_mpic;
44662306a36Sopenharmony_ci		high_handler = NULL;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS,
45062306a36Sopenharmony_ci					       &qe_ic_host_ops, qe_ic);
45162306a36Sopenharmony_ci	if (qe_ic->irqhost == NULL) {
45262306a36Sopenharmony_ci		dev_err(dev, "failed to add irq domain\n");
45362306a36Sopenharmony_ci		return -ENODEV;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	qe_ic_write(qe_ic->regs, QEIC_CICR, 0);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	irq_set_handler_data(qe_ic->virq_low, qe_ic);
45962306a36Sopenharmony_ci	irq_set_chained_handler(qe_ic->virq_low, low_handler);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (high_handler) {
46262306a36Sopenharmony_ci		irq_set_handler_data(qe_ic->virq_high, qe_ic);
46362306a36Sopenharmony_ci		irq_set_chained_handler(qe_ic->virq_high, high_handler);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_cistatic const struct of_device_id qe_ic_ids[] = {
46862306a36Sopenharmony_ci	{ .compatible = "fsl,qe-ic"},
46962306a36Sopenharmony_ci	{ .type = "qeic"},
47062306a36Sopenharmony_ci	{},
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic struct platform_driver qe_ic_driver =
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	.driver	= {
47662306a36Sopenharmony_ci		.name		= "qe-ic",
47762306a36Sopenharmony_ci		.of_match_table	= qe_ic_ids,
47862306a36Sopenharmony_ci	},
47962306a36Sopenharmony_ci	.probe	= qe_ic_init,
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int __init qe_ic_of_init(void)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	platform_driver_register(&qe_ic_driver);
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_cisubsys_initcall(qe_ic_of_init);
488