18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/powerpc/sysdev/qe_lib/qe_ic.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Freescale Semiconductor, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Li Yang <leoli@freescale.com>
88c2ecf20Sopenharmony_ci * Based on code from Shlomi Gridish <gridish@freescale.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * QUICC ENGINE Interrupt Controller
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/irq.h>
198c2ecf20Sopenharmony_ci#include <linux/reboot.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/stddef.h>
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/signal.h>
248c2ecf20Sopenharmony_ci#include <linux/device.h>
258c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
268c2ecf20Sopenharmony_ci#include <asm/irq.h>
278c2ecf20Sopenharmony_ci#include <asm/io.h>
288c2ecf20Sopenharmony_ci#include <soc/fsl/qe/qe.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define NR_QE_IC_INTS		64
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* QE IC registers offset */
338c2ecf20Sopenharmony_ci#define QEIC_CICR		0x00
348c2ecf20Sopenharmony_ci#define QEIC_CIVEC		0x04
358c2ecf20Sopenharmony_ci#define QEIC_CIPXCC		0x10
368c2ecf20Sopenharmony_ci#define QEIC_CIPYCC		0x14
378c2ecf20Sopenharmony_ci#define QEIC_CIPWCC		0x18
388c2ecf20Sopenharmony_ci#define QEIC_CIPZCC		0x1c
398c2ecf20Sopenharmony_ci#define QEIC_CIMR		0x20
408c2ecf20Sopenharmony_ci#define QEIC_CRIMR		0x24
418c2ecf20Sopenharmony_ci#define QEIC_CIPRTA		0x30
428c2ecf20Sopenharmony_ci#define QEIC_CIPRTB		0x34
438c2ecf20Sopenharmony_ci#define QEIC_CHIVEC		0x60
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct qe_ic {
468c2ecf20Sopenharmony_ci	/* Control registers offset */
478c2ecf20Sopenharmony_ci	__be32 __iomem *regs;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* The remapper for this QEIC */
508c2ecf20Sopenharmony_ci	struct irq_domain *irqhost;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* The "linux" controller struct */
538c2ecf20Sopenharmony_ci	struct irq_chip hc_irq;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* VIRQ numbers of QE high/low irqs */
568c2ecf20Sopenharmony_ci	unsigned int virq_high;
578c2ecf20Sopenharmony_ci	unsigned int virq_low;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * QE interrupt controller internal structure
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistruct qe_ic_info {
648c2ecf20Sopenharmony_ci	/* Location of this source at the QIMR register */
658c2ecf20Sopenharmony_ci	u32	mask;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* Mask register offset */
688c2ecf20Sopenharmony_ci	u32	mask_reg;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/*
718c2ecf20Sopenharmony_ci	 * For grouped interrupts sources - the interrupt code as
728c2ecf20Sopenharmony_ci	 * appears at the group priority register
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	u8	pri_code;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Group priority register offset */
778c2ecf20Sopenharmony_ci	u32	pri_reg;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(qe_ic_lock);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct qe_ic_info qe_ic_info[] = {
838c2ecf20Sopenharmony_ci	[1] = {
848c2ecf20Sopenharmony_ci	       .mask = 0x00008000,
858c2ecf20Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
868c2ecf20Sopenharmony_ci	       .pri_code = 0,
878c2ecf20Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
888c2ecf20Sopenharmony_ci	       },
898c2ecf20Sopenharmony_ci	[2] = {
908c2ecf20Sopenharmony_ci	       .mask = 0x00004000,
918c2ecf20Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
928c2ecf20Sopenharmony_ci	       .pri_code = 1,
938c2ecf20Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
948c2ecf20Sopenharmony_ci	       },
958c2ecf20Sopenharmony_ci	[3] = {
968c2ecf20Sopenharmony_ci	       .mask = 0x00002000,
978c2ecf20Sopenharmony_ci	       .mask_reg = QEIC_CIMR,
988c2ecf20Sopenharmony_ci	       .pri_code = 2,
998c2ecf20Sopenharmony_ci	       .pri_reg = QEIC_CIPWCC,
1008c2ecf20Sopenharmony_ci	       },
1018c2ecf20Sopenharmony_ci	[10] = {
1028c2ecf20Sopenharmony_ci		.mask = 0x00000040,
1038c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1048c2ecf20Sopenharmony_ci		.pri_code = 1,
1058c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1068c2ecf20Sopenharmony_ci		},
1078c2ecf20Sopenharmony_ci	[11] = {
1088c2ecf20Sopenharmony_ci		.mask = 0x00000020,
1098c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1108c2ecf20Sopenharmony_ci		.pri_code = 2,
1118c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1128c2ecf20Sopenharmony_ci		},
1138c2ecf20Sopenharmony_ci	[12] = {
1148c2ecf20Sopenharmony_ci		.mask = 0x00000010,
1158c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1168c2ecf20Sopenharmony_ci		.pri_code = 3,
1178c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1188c2ecf20Sopenharmony_ci		},
1198c2ecf20Sopenharmony_ci	[13] = {
1208c2ecf20Sopenharmony_ci		.mask = 0x00000008,
1218c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1228c2ecf20Sopenharmony_ci		.pri_code = 4,
1238c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1248c2ecf20Sopenharmony_ci		},
1258c2ecf20Sopenharmony_ci	[14] = {
1268c2ecf20Sopenharmony_ci		.mask = 0x00000004,
1278c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1288c2ecf20Sopenharmony_ci		.pri_code = 5,
1298c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1308c2ecf20Sopenharmony_ci		},
1318c2ecf20Sopenharmony_ci	[15] = {
1328c2ecf20Sopenharmony_ci		.mask = 0x00000002,
1338c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1348c2ecf20Sopenharmony_ci		.pri_code = 6,
1358c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPZCC,
1368c2ecf20Sopenharmony_ci		},
1378c2ecf20Sopenharmony_ci	[20] = {
1388c2ecf20Sopenharmony_ci		.mask = 0x10000000,
1398c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
1408c2ecf20Sopenharmony_ci		.pri_code = 3,
1418c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPRTA,
1428c2ecf20Sopenharmony_ci		},
1438c2ecf20Sopenharmony_ci	[25] = {
1448c2ecf20Sopenharmony_ci		.mask = 0x00800000,
1458c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
1468c2ecf20Sopenharmony_ci		.pri_code = 0,
1478c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
1488c2ecf20Sopenharmony_ci		},
1498c2ecf20Sopenharmony_ci	[26] = {
1508c2ecf20Sopenharmony_ci		.mask = 0x00400000,
1518c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
1528c2ecf20Sopenharmony_ci		.pri_code = 1,
1538c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
1548c2ecf20Sopenharmony_ci		},
1558c2ecf20Sopenharmony_ci	[27] = {
1568c2ecf20Sopenharmony_ci		.mask = 0x00200000,
1578c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
1588c2ecf20Sopenharmony_ci		.pri_code = 2,
1598c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
1608c2ecf20Sopenharmony_ci		},
1618c2ecf20Sopenharmony_ci	[28] = {
1628c2ecf20Sopenharmony_ci		.mask = 0x00100000,
1638c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CRIMR,
1648c2ecf20Sopenharmony_ci		.pri_code = 3,
1658c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPRTB,
1668c2ecf20Sopenharmony_ci		},
1678c2ecf20Sopenharmony_ci	[32] = {
1688c2ecf20Sopenharmony_ci		.mask = 0x80000000,
1698c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1708c2ecf20Sopenharmony_ci		.pri_code = 0,
1718c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
1728c2ecf20Sopenharmony_ci		},
1738c2ecf20Sopenharmony_ci	[33] = {
1748c2ecf20Sopenharmony_ci		.mask = 0x40000000,
1758c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1768c2ecf20Sopenharmony_ci		.pri_code = 1,
1778c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
1788c2ecf20Sopenharmony_ci		},
1798c2ecf20Sopenharmony_ci	[34] = {
1808c2ecf20Sopenharmony_ci		.mask = 0x20000000,
1818c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1828c2ecf20Sopenharmony_ci		.pri_code = 2,
1838c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
1848c2ecf20Sopenharmony_ci		},
1858c2ecf20Sopenharmony_ci	[35] = {
1868c2ecf20Sopenharmony_ci		.mask = 0x10000000,
1878c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1888c2ecf20Sopenharmony_ci		.pri_code = 3,
1898c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
1908c2ecf20Sopenharmony_ci		},
1918c2ecf20Sopenharmony_ci	[36] = {
1928c2ecf20Sopenharmony_ci		.mask = 0x08000000,
1938c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
1948c2ecf20Sopenharmony_ci		.pri_code = 4,
1958c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPXCC,
1968c2ecf20Sopenharmony_ci		},
1978c2ecf20Sopenharmony_ci	[40] = {
1988c2ecf20Sopenharmony_ci		.mask = 0x00800000,
1998c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
2008c2ecf20Sopenharmony_ci		.pri_code = 0,
2018c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
2028c2ecf20Sopenharmony_ci		},
2038c2ecf20Sopenharmony_ci	[41] = {
2048c2ecf20Sopenharmony_ci		.mask = 0x00400000,
2058c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
2068c2ecf20Sopenharmony_ci		.pri_code = 1,
2078c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
2088c2ecf20Sopenharmony_ci		},
2098c2ecf20Sopenharmony_ci	[42] = {
2108c2ecf20Sopenharmony_ci		.mask = 0x00200000,
2118c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
2128c2ecf20Sopenharmony_ci		.pri_code = 2,
2138c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
2148c2ecf20Sopenharmony_ci		},
2158c2ecf20Sopenharmony_ci	[43] = {
2168c2ecf20Sopenharmony_ci		.mask = 0x00100000,
2178c2ecf20Sopenharmony_ci		.mask_reg = QEIC_CIMR,
2188c2ecf20Sopenharmony_ci		.pri_code = 3,
2198c2ecf20Sopenharmony_ci		.pri_reg = QEIC_CIPYCC,
2208c2ecf20Sopenharmony_ci		},
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic inline u32 qe_ic_read(__be32  __iomem *base, unsigned int reg)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	return qe_ioread32be(base + (reg >> 2));
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic inline void qe_ic_write(__be32  __iomem *base, unsigned int reg,
2298c2ecf20Sopenharmony_ci			       u32 value)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	qe_iowrite32be(value, base + (reg >> 2));
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	return irq_get_chip_data(virq);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	return irq_data_get_irq_chip_data(d);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void qe_ic_unmask_irq(struct irq_data *d)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
2478c2ecf20Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
2488c2ecf20Sopenharmony_ci	unsigned long flags;
2498c2ecf20Sopenharmony_ci	u32 temp;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&qe_ic_lock, flags);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
2548c2ecf20Sopenharmony_ci	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
2558c2ecf20Sopenharmony_ci		    temp | qe_ic_info[src].mask);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void qe_ic_mask_irq(struct irq_data *d)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
2638c2ecf20Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
2648c2ecf20Sopenharmony_ci	unsigned long flags;
2658c2ecf20Sopenharmony_ci	u32 temp;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&qe_ic_lock, flags);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
2708c2ecf20Sopenharmony_ci	qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
2718c2ecf20Sopenharmony_ci		    temp & ~qe_ic_info[src].mask);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Flush the above write before enabling interrupts; otherwise,
2748c2ecf20Sopenharmony_ci	 * spurious interrupts will sometimes happen.  To be 100% sure
2758c2ecf20Sopenharmony_ci	 * that the write has reached the device before interrupts are
2768c2ecf20Sopenharmony_ci	 * enabled, the mask register would have to be read back; however,
2778c2ecf20Sopenharmony_ci	 * this is not required for correctness, only to avoid wasting
2788c2ecf20Sopenharmony_ci	 * time on a large number of spurious interrupts.  In testing,
2798c2ecf20Sopenharmony_ci	 * a sync reduced the observed spurious interrupts to zero.
2808c2ecf20Sopenharmony_ci	 */
2818c2ecf20Sopenharmony_ci	mb();
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic struct irq_chip qe_ic_irq_chip = {
2878c2ecf20Sopenharmony_ci	.name = "QEIC",
2888c2ecf20Sopenharmony_ci	.irq_unmask = qe_ic_unmask_irq,
2898c2ecf20Sopenharmony_ci	.irq_mask = qe_ic_mask_irq,
2908c2ecf20Sopenharmony_ci	.irq_mask_ack = qe_ic_mask_irq,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int qe_ic_host_match(struct irq_domain *h, struct device_node *node,
2948c2ecf20Sopenharmony_ci			    enum irq_domain_bus_token bus_token)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	/* Exact match, unless qe_ic node is NULL */
2978c2ecf20Sopenharmony_ci	struct device_node *of_node = irq_domain_get_of_node(h);
2988c2ecf20Sopenharmony_ci	return of_node == NULL || of_node == node;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
3028c2ecf20Sopenharmony_ci			  irq_hw_number_t hw)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = h->host_data;
3058c2ecf20Sopenharmony_ci	struct irq_chip *chip;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (hw >= ARRAY_SIZE(qe_ic_info)) {
3088c2ecf20Sopenharmony_ci		pr_err("%s: Invalid hw irq number for QEIC\n", __func__);
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (qe_ic_info[hw].mask == 0) {
3138c2ecf20Sopenharmony_ci		printk(KERN_ERR "Can't map reserved IRQ\n");
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	/* Default chip */
3178c2ecf20Sopenharmony_ci	chip = &qe_ic->hc_irq;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	irq_set_chip_data(virq, qe_ic);
3208c2ecf20Sopenharmony_ci	irq_set_status_flags(virq, IRQ_LEVEL);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, chip, handle_level_irq);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic const struct irq_domain_ops qe_ic_host_ops = {
3288c2ecf20Sopenharmony_ci	.match = qe_ic_host_match,
3298c2ecf20Sopenharmony_ci	.map = qe_ic_host_map,
3308c2ecf20Sopenharmony_ci	.xlate = irq_domain_xlate_onetwocell,
3318c2ecf20Sopenharmony_ci};
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/* Return an interrupt vector or 0 if no interrupt is pending. */
3348c2ecf20Sopenharmony_cistatic unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	int irq;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	BUG_ON(qe_ic == NULL);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* get the interrupt source vector. */
3418c2ecf20Sopenharmony_ci	irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (irq == 0)
3448c2ecf20Sopenharmony_ci		return 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return irq_linear_revmap(qe_ic->irqhost, irq);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/* Return an interrupt vector or 0 if no interrupt is pending. */
3508c2ecf20Sopenharmony_cistatic unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	int irq;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	BUG_ON(qe_ic == NULL);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* get the interrupt source vector. */
3578c2ecf20Sopenharmony_ci	irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (irq == 0)
3608c2ecf20Sopenharmony_ci		return 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return irq_linear_revmap(qe_ic->irqhost, irq);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void qe_ic_cascade_low(struct irq_desc *desc)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
3688c2ecf20Sopenharmony_ci	unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic);
3698c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (cascade_irq != 0)
3728c2ecf20Sopenharmony_ci		generic_handle_irq(cascade_irq);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (chip->irq_eoi)
3758c2ecf20Sopenharmony_ci		chip->irq_eoi(&desc->irq_data);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic void qe_ic_cascade_high(struct irq_desc *desc)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
3818c2ecf20Sopenharmony_ci	unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic);
3828c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (cascade_irq != 0)
3858c2ecf20Sopenharmony_ci		generic_handle_irq(cascade_irq);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (chip->irq_eoi)
3888c2ecf20Sopenharmony_ci		chip->irq_eoi(&desc->irq_data);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void qe_ic_cascade_muxed_mpic(struct irq_desc *desc)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
3948c2ecf20Sopenharmony_ci	unsigned int cascade_irq;
3958c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	cascade_irq = qe_ic_get_high_irq(qe_ic);
3988c2ecf20Sopenharmony_ci	if (cascade_irq == 0)
3998c2ecf20Sopenharmony_ci		cascade_irq = qe_ic_get_low_irq(qe_ic);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (cascade_irq != 0)
4028c2ecf20Sopenharmony_ci		generic_handle_irq(cascade_irq);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	chip->irq_eoi(&desc->irq_data);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void __init qe_ic_init(struct device_node *node)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	void (*low_handler)(struct irq_desc *desc);
4108c2ecf20Sopenharmony_ci	void (*high_handler)(struct irq_desc *desc);
4118c2ecf20Sopenharmony_ci	struct qe_ic *qe_ic;
4128c2ecf20Sopenharmony_ci	struct resource res;
4138c2ecf20Sopenharmony_ci	u32 ret;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ret = of_address_to_resource(node, 0, &res);
4168c2ecf20Sopenharmony_ci	if (ret)
4178c2ecf20Sopenharmony_ci		return;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL);
4208c2ecf20Sopenharmony_ci	if (qe_ic == NULL)
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS,
4248c2ecf20Sopenharmony_ci					       &qe_ic_host_ops, qe_ic);
4258c2ecf20Sopenharmony_ci	if (qe_ic->irqhost == NULL) {
4268c2ecf20Sopenharmony_ci		kfree(qe_ic);
4278c2ecf20Sopenharmony_ci		return;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	qe_ic->regs = ioremap(res.start, resource_size(&res));
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	qe_ic->hc_irq = qe_ic_irq_chip;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	qe_ic->virq_high = irq_of_parse_and_map(node, 0);
4358c2ecf20Sopenharmony_ci	qe_ic->virq_low = irq_of_parse_and_map(node, 1);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!qe_ic->virq_low) {
4388c2ecf20Sopenharmony_ci		printk(KERN_ERR "Failed to map QE_IC low IRQ\n");
4398c2ecf20Sopenharmony_ci		kfree(qe_ic);
4408c2ecf20Sopenharmony_ci		return;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	if (qe_ic->virq_high != qe_ic->virq_low) {
4438c2ecf20Sopenharmony_ci		low_handler = qe_ic_cascade_low;
4448c2ecf20Sopenharmony_ci		high_handler = qe_ic_cascade_high;
4458c2ecf20Sopenharmony_ci	} else {
4468c2ecf20Sopenharmony_ci		low_handler = qe_ic_cascade_muxed_mpic;
4478c2ecf20Sopenharmony_ci		high_handler = NULL;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	qe_ic_write(qe_ic->regs, QEIC_CICR, 0);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	irq_set_handler_data(qe_ic->virq_low, qe_ic);
4538c2ecf20Sopenharmony_ci	irq_set_chained_handler(qe_ic->virq_low, low_handler);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (qe_ic->virq_high && qe_ic->virq_high != qe_ic->virq_low) {
4568c2ecf20Sopenharmony_ci		irq_set_handler_data(qe_ic->virq_high, qe_ic);
4578c2ecf20Sopenharmony_ci		irq_set_chained_handler(qe_ic->virq_high, high_handler);
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int __init qe_ic_of_init(void)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct device_node *np;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
4668c2ecf20Sopenharmony_ci	if (!np) {
4678c2ecf20Sopenharmony_ci		np = of_find_node_by_type(NULL, "qeic");
4688c2ecf20Sopenharmony_ci		if (!np)
4698c2ecf20Sopenharmony_ci			return -ENODEV;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	qe_ic_init(np);
4728c2ecf20Sopenharmony_ci	of_node_put(np);
4738c2ecf20Sopenharmony_ci	return 0;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_cisubsys_initcall(qe_ic_of_init);
476