18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Shared interrupt handling code for IPR and INTC2 types of IRQs.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 Magnus Damm
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 - 2012 Paul Mundt
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on intc2.c and ipr.c
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 1999  Niibe Yutaka & Takeshi Yaegashi
108c2ecf20Sopenharmony_ci * Copyright (C) 2000  Kazumoto Kojima
118c2ecf20Sopenharmony_ci * Copyright (C) 2001  David J. Mckay (david.mckay@st.com)
128c2ecf20Sopenharmony_ci * Copyright (C) 2003  Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
138c2ecf20Sopenharmony_ci * Copyright (C) 2005, 2006  Paul Mundt
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
168c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
178c2ecf20Sopenharmony_ci * for more details.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "intc: " fmt
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/irq.h>
238c2ecf20Sopenharmony_ci#include <linux/io.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/stat.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/sh_intc.h>
288c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
298c2ecf20Sopenharmony_ci#include <linux/device.h>
308c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
318c2ecf20Sopenharmony_ci#include <linux/list.h>
328c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
338c2ecf20Sopenharmony_ci#include <linux/radix-tree.h>
348c2ecf20Sopenharmony_ci#include <linux/export.h>
358c2ecf20Sopenharmony_ci#include <linux/sort.h>
368c2ecf20Sopenharmony_ci#include "internals.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciLIST_HEAD(intc_list);
398c2ecf20Sopenharmony_ciDEFINE_RAW_SPINLOCK(intc_big_lock);
408c2ecf20Sopenharmony_cistatic unsigned int nr_intc_controllers;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Default priority level
448c2ecf20Sopenharmony_ci * - this needs to be at least 2 for 5-bit priorities on 7780
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic unsigned int default_prio_level = 2;	/* 2 - 16 */
478c2ecf20Sopenharmony_cistatic unsigned int intc_prio_level[INTC_NR_IRQS];	/* for now */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciunsigned int intc_get_dfl_prio_level(void)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return default_prio_level;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciunsigned int intc_get_prio_level(unsigned int irq)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return intc_prio_level[irq];
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_civoid intc_set_prio_level(unsigned int irq, unsigned int level)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned long flags;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&intc_big_lock, flags);
648c2ecf20Sopenharmony_ci	intc_prio_level[irq] = level;
658c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&intc_big_lock, flags);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void intc_redirect_irq(struct irq_desc *desc)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	generic_handle_irq((unsigned int)irq_desc_get_handler_data(desc));
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void __init intc_register_irq(struct intc_desc *desc,
748c2ecf20Sopenharmony_ci				     struct intc_desc_int *d,
758c2ecf20Sopenharmony_ci				     intc_enum enum_id,
768c2ecf20Sopenharmony_ci				     unsigned int irq)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct intc_handle_int *hp;
798c2ecf20Sopenharmony_ci	struct irq_data *irq_data;
808c2ecf20Sopenharmony_ci	unsigned int data[2], primary;
818c2ecf20Sopenharmony_ci	unsigned long flags;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&intc_big_lock, flags);
848c2ecf20Sopenharmony_ci	radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));
858c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&intc_big_lock, flags);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Prefer single interrupt source bitmap over other combinations:
898c2ecf20Sopenharmony_ci	 *
908c2ecf20Sopenharmony_ci	 * 1. bitmap, single interrupt source
918c2ecf20Sopenharmony_ci	 * 2. priority, single interrupt source
928c2ecf20Sopenharmony_ci	 * 3. bitmap, multiple interrupt sources (groups)
938c2ecf20Sopenharmony_ci	 * 4. priority, multiple interrupt sources (groups)
948c2ecf20Sopenharmony_ci	 */
958c2ecf20Sopenharmony_ci	data[0] = intc_get_mask_handle(desc, d, enum_id, 0);
968c2ecf20Sopenharmony_ci	data[1] = intc_get_prio_handle(desc, d, enum_id, 0);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	primary = 0;
998c2ecf20Sopenharmony_ci	if (!data[0] && data[1])
1008c2ecf20Sopenharmony_ci		primary = 1;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!data[0] && !data[1])
1038c2ecf20Sopenharmony_ci		pr_warn("missing unique irq mask for irq %d (vect 0x%04x)\n",
1048c2ecf20Sopenharmony_ci			irq, irq2evt(irq));
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1);
1078c2ecf20Sopenharmony_ci	data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (!data[primary])
1108c2ecf20Sopenharmony_ci		primary ^= 1;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	BUG_ON(!data[primary]); /* must have primary masking method */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	irq_data = irq_get_irq_data(irq);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	disable_irq_nosync(irq);
1178c2ecf20Sopenharmony_ci	irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq,
1188c2ecf20Sopenharmony_ci				      "level");
1198c2ecf20Sopenharmony_ci	irq_set_chip_data(irq, (void *)data[primary]);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * set priority level
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	intc_set_prio_level(irq, intc_get_dfl_prio_level());
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* enable secondary masking method if present */
1278c2ecf20Sopenharmony_ci	if (data[!primary])
1288c2ecf20Sopenharmony_ci		_intc_enable(irq_data, data[!primary]);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* add irq to d->prio list if priority is available */
1318c2ecf20Sopenharmony_ci	if (data[1]) {
1328c2ecf20Sopenharmony_ci		hp = d->prio + d->nr_prio;
1338c2ecf20Sopenharmony_ci		hp->irq = irq;
1348c2ecf20Sopenharmony_ci		hp->handle = data[1];
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		if (primary) {
1378c2ecf20Sopenharmony_ci			/*
1388c2ecf20Sopenharmony_ci			 * only secondary priority should access registers, so
1398c2ecf20Sopenharmony_ci			 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
1408c2ecf20Sopenharmony_ci			 */
1418c2ecf20Sopenharmony_ci			hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
1428c2ecf20Sopenharmony_ci			hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci		d->nr_prio++;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* add irq to d->sense list if sense is available */
1488c2ecf20Sopenharmony_ci	data[0] = intc_get_sense_handle(desc, d, enum_id);
1498c2ecf20Sopenharmony_ci	if (data[0]) {
1508c2ecf20Sopenharmony_ci		(d->sense + d->nr_sense)->irq = irq;
1518c2ecf20Sopenharmony_ci		(d->sense + d->nr_sense)->handle = data[0];
1528c2ecf20Sopenharmony_ci		d->nr_sense++;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* irq should be disabled by default */
1568c2ecf20Sopenharmony_ci	d->chip.irq_mask(irq_data);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	intc_set_ack_handle(irq, desc, d, enum_id);
1598c2ecf20Sopenharmony_ci	intc_set_dist_handle(irq, desc, d, enum_id);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	activate_irq(irq);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic unsigned int __init save_reg(struct intc_desc_int *d,
1658c2ecf20Sopenharmony_ci				    unsigned int cnt,
1668c2ecf20Sopenharmony_ci				    unsigned long value,
1678c2ecf20Sopenharmony_ci				    unsigned int smp)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	if (value) {
1708c2ecf20Sopenharmony_ci		value = intc_phys_to_virt(d, value);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		d->reg[cnt] = value;
1738c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
1748c2ecf20Sopenharmony_ci		d->smp[cnt] = smp;
1758c2ecf20Sopenharmony_ci#endif
1768c2ecf20Sopenharmony_ci		return 1;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciint __init register_intc_controller(struct intc_desc *desc)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	unsigned int i, k, smp;
1858c2ecf20Sopenharmony_ci	struct intc_hw_desc *hw = &desc->hw;
1868c2ecf20Sopenharmony_ci	struct intc_desc_int *d;
1878c2ecf20Sopenharmony_ci	struct resource *res;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pr_info("Registered controller '%s' with %u IRQs\n",
1908c2ecf20Sopenharmony_ci		desc->name, hw->nr_vectors);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d), GFP_NOWAIT);
1938c2ecf20Sopenharmony_ci	if (!d)
1948c2ecf20Sopenharmony_ci		goto err0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&d->list);
1978c2ecf20Sopenharmony_ci	list_add_tail(&d->list, &intc_list);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	raw_spin_lock_init(&d->lock);
2008c2ecf20Sopenharmony_ci	INIT_RADIX_TREE(&d->tree, GFP_ATOMIC);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	d->index = nr_intc_controllers;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (desc->num_resources) {
2058c2ecf20Sopenharmony_ci		d->nr_windows = desc->num_resources;
2068c2ecf20Sopenharmony_ci		d->window = kcalloc(d->nr_windows, sizeof(*d->window),
2078c2ecf20Sopenharmony_ci				    GFP_NOWAIT);
2088c2ecf20Sopenharmony_ci		if (!d->window)
2098c2ecf20Sopenharmony_ci			goto err1;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		for (k = 0; k < d->nr_windows; k++) {
2128c2ecf20Sopenharmony_ci			res = desc->resource + k;
2138c2ecf20Sopenharmony_ci			WARN_ON(resource_type(res) != IORESOURCE_MEM);
2148c2ecf20Sopenharmony_ci			d->window[k].phys = res->start;
2158c2ecf20Sopenharmony_ci			d->window[k].size = resource_size(res);
2168c2ecf20Sopenharmony_ci			d->window[k].virt = ioremap(res->start,
2178c2ecf20Sopenharmony_ci							 resource_size(res));
2188c2ecf20Sopenharmony_ci			if (!d->window[k].virt)
2198c2ecf20Sopenharmony_ci				goto err2;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
2248c2ecf20Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING
2258c2ecf20Sopenharmony_ci	if (d->nr_reg)
2268c2ecf20Sopenharmony_ci		d->nr_reg += hw->nr_mask_regs;
2278c2ecf20Sopenharmony_ci#endif
2288c2ecf20Sopenharmony_ci	d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
2298c2ecf20Sopenharmony_ci	d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
2308c2ecf20Sopenharmony_ci	d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
2318c2ecf20Sopenharmony_ci	d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	d->reg = kcalloc(d->nr_reg, sizeof(*d->reg), GFP_NOWAIT);
2348c2ecf20Sopenharmony_ci	if (!d->reg)
2358c2ecf20Sopenharmony_ci		goto err2;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
2388c2ecf20Sopenharmony_ci	d->smp = kcalloc(d->nr_reg, sizeof(*d->smp), GFP_NOWAIT);
2398c2ecf20Sopenharmony_ci	if (!d->smp)
2408c2ecf20Sopenharmony_ci		goto err3;
2418c2ecf20Sopenharmony_ci#endif
2428c2ecf20Sopenharmony_ci	k = 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (hw->mask_regs) {
2458c2ecf20Sopenharmony_ci		for (i = 0; i < hw->nr_mask_regs; i++) {
2468c2ecf20Sopenharmony_ci			smp = IS_SMP(hw->mask_regs[i]);
2478c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
2488c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
2498c2ecf20Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING
2508c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0);
2518c2ecf20Sopenharmony_ci#endif
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (hw->prio_regs) {
2568c2ecf20Sopenharmony_ci		d->prio = kcalloc(hw->nr_vectors, sizeof(*d->prio),
2578c2ecf20Sopenharmony_ci				  GFP_NOWAIT);
2588c2ecf20Sopenharmony_ci		if (!d->prio)
2598c2ecf20Sopenharmony_ci			goto err4;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		for (i = 0; i < hw->nr_prio_regs; i++) {
2628c2ecf20Sopenharmony_ci			smp = IS_SMP(hw->prio_regs[i]);
2638c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
2648c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio),
2688c2ecf20Sopenharmony_ci		     intc_handle_int_cmp, NULL);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (hw->sense_regs) {
2728c2ecf20Sopenharmony_ci		d->sense = kcalloc(hw->nr_vectors, sizeof(*d->sense),
2738c2ecf20Sopenharmony_ci				   GFP_NOWAIT);
2748c2ecf20Sopenharmony_ci		if (!d->sense)
2758c2ecf20Sopenharmony_ci			goto err5;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		for (i = 0; i < hw->nr_sense_regs; i++)
2788c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->sense_regs[i].reg, 0);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense),
2818c2ecf20Sopenharmony_ci		     intc_handle_int_cmp, NULL);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (hw->subgroups)
2858c2ecf20Sopenharmony_ci		for (i = 0; i < hw->nr_subgroups; i++)
2868c2ecf20Sopenharmony_ci			if (hw->subgroups[i].reg)
2878c2ecf20Sopenharmony_ci				k+= save_reg(d, k, hw->subgroups[i].reg, 0);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip));
2908c2ecf20Sopenharmony_ci	d->chip.name = desc->name;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (hw->ack_regs)
2938c2ecf20Sopenharmony_ci		for (i = 0; i < hw->nr_ack_regs; i++)
2948c2ecf20Sopenharmony_ci			k += save_reg(d, k, hw->ack_regs[i].set_reg, 0);
2958c2ecf20Sopenharmony_ci	else
2968c2ecf20Sopenharmony_ci		d->chip.irq_mask_ack = d->chip.irq_disable;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* disable bits matching force_disable before registering irqs */
2998c2ecf20Sopenharmony_ci	if (desc->force_disable)
3008c2ecf20Sopenharmony_ci		intc_enable_disable_enum(desc, d, desc->force_disable, 0);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* disable bits matching force_enable before registering irqs */
3038c2ecf20Sopenharmony_ci	if (desc->force_enable)
3048c2ecf20Sopenharmony_ci		intc_enable_disable_enum(desc, d, desc->force_enable, 0);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	intc_irq_domain_init(d, hw);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* register the vectors one by one */
3118c2ecf20Sopenharmony_ci	for (i = 0; i < hw->nr_vectors; i++) {
3128c2ecf20Sopenharmony_ci		struct intc_vect *vect = hw->vectors + i;
3138c2ecf20Sopenharmony_ci		unsigned int irq = evt2irq(vect->vect);
3148c2ecf20Sopenharmony_ci		int res;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		if (!vect->enum_id)
3178c2ecf20Sopenharmony_ci			continue;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		res = irq_create_identity_mapping(d->domain, irq);
3208c2ecf20Sopenharmony_ci		if (unlikely(res)) {
3218c2ecf20Sopenharmony_ci			if (res == -EEXIST) {
3228c2ecf20Sopenharmony_ci				res = irq_domain_associate(d->domain, irq, irq);
3238c2ecf20Sopenharmony_ci				if (unlikely(res)) {
3248c2ecf20Sopenharmony_ci					pr_err("domain association failure\n");
3258c2ecf20Sopenharmony_ci					continue;
3268c2ecf20Sopenharmony_ci				}
3278c2ecf20Sopenharmony_ci			} else {
3288c2ecf20Sopenharmony_ci				pr_err("can't identity map IRQ %d\n", irq);
3298c2ecf20Sopenharmony_ci				continue;
3308c2ecf20Sopenharmony_ci			}
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		intc_irq_xlate_set(irq, vect->enum_id, d);
3348c2ecf20Sopenharmony_ci		intc_register_irq(desc, d, vect->enum_id, irq);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		for (k = i + 1; k < hw->nr_vectors; k++) {
3378c2ecf20Sopenharmony_ci			struct intc_vect *vect2 = hw->vectors + k;
3388c2ecf20Sopenharmony_ci			unsigned int irq2 = evt2irq(vect2->vect);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci			if (vect->enum_id != vect2->enum_id)
3418c2ecf20Sopenharmony_ci				continue;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci			/*
3448c2ecf20Sopenharmony_ci			 * In the case of multi-evt handling and sparse
3458c2ecf20Sopenharmony_ci			 * IRQ support, each vector still needs to have
3468c2ecf20Sopenharmony_ci			 * its own backing irq_desc.
3478c2ecf20Sopenharmony_ci			 */
3488c2ecf20Sopenharmony_ci			res = irq_create_identity_mapping(d->domain, irq2);
3498c2ecf20Sopenharmony_ci			if (unlikely(res)) {
3508c2ecf20Sopenharmony_ci				if (res == -EEXIST) {
3518c2ecf20Sopenharmony_ci					res = irq_domain_associate(d->domain,
3528c2ecf20Sopenharmony_ci								   irq2, irq2);
3538c2ecf20Sopenharmony_ci					if (unlikely(res)) {
3548c2ecf20Sopenharmony_ci						pr_err("domain association "
3558c2ecf20Sopenharmony_ci						       "failure\n");
3568c2ecf20Sopenharmony_ci						continue;
3578c2ecf20Sopenharmony_ci					}
3588c2ecf20Sopenharmony_ci				} else {
3598c2ecf20Sopenharmony_ci					pr_err("can't identity map IRQ %d\n",
3608c2ecf20Sopenharmony_ci					       irq);
3618c2ecf20Sopenharmony_ci					continue;
3628c2ecf20Sopenharmony_ci				}
3638c2ecf20Sopenharmony_ci			}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci			vect2->enum_id = 0;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci			/* redirect this interrupts to the first one */
3688c2ecf20Sopenharmony_ci			irq_set_chip(irq2, &dummy_irq_chip);
3698c2ecf20Sopenharmony_ci			irq_set_chained_handler_and_data(irq2,
3708c2ecf20Sopenharmony_ci							 intc_redirect_irq,
3718c2ecf20Sopenharmony_ci							 (void *)irq);
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	intc_subgroup_init(desc, d);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* enable bits matching force_enable after registering irqs */
3788c2ecf20Sopenharmony_ci	if (desc->force_enable)
3798c2ecf20Sopenharmony_ci		intc_enable_disable_enum(desc, d, desc->force_enable, 1);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	d->skip_suspend = desc->skip_syscore_suspend;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	nr_intc_controllers++;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_cierr5:
3878c2ecf20Sopenharmony_ci	kfree(d->prio);
3888c2ecf20Sopenharmony_cierr4:
3898c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
3908c2ecf20Sopenharmony_ci	kfree(d->smp);
3918c2ecf20Sopenharmony_cierr3:
3928c2ecf20Sopenharmony_ci#endif
3938c2ecf20Sopenharmony_ci	kfree(d->reg);
3948c2ecf20Sopenharmony_cierr2:
3958c2ecf20Sopenharmony_ci	for (k = 0; k < d->nr_windows; k++)
3968c2ecf20Sopenharmony_ci		if (d->window[k].virt)
3978c2ecf20Sopenharmony_ci			iounmap(d->window[k].virt);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	kfree(d->window);
4008c2ecf20Sopenharmony_cierr1:
4018c2ecf20Sopenharmony_ci	kfree(d);
4028c2ecf20Sopenharmony_cierr0:
4038c2ecf20Sopenharmony_ci	pr_err("unable to allocate INTC memory\n");
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return -ENOMEM;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int intc_suspend(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct intc_desc_int *d;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	list_for_each_entry(d, &intc_list, list) {
4138c2ecf20Sopenharmony_ci		int irq;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		if (d->skip_suspend)
4168c2ecf20Sopenharmony_ci			continue;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		/* enable wakeup irqs belonging to this intc controller */
4198c2ecf20Sopenharmony_ci		for_each_active_irq(irq) {
4208c2ecf20Sopenharmony_ci			struct irq_data *data;
4218c2ecf20Sopenharmony_ci			struct irq_chip *chip;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			data = irq_get_irq_data(irq);
4248c2ecf20Sopenharmony_ci			chip = irq_data_get_irq_chip(data);
4258c2ecf20Sopenharmony_ci			if (chip != &d->chip)
4268c2ecf20Sopenharmony_ci				continue;
4278c2ecf20Sopenharmony_ci			if (irqd_is_wakeup_set(data))
4288c2ecf20Sopenharmony_ci				chip->irq_enable(data);
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void intc_resume(void)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct intc_desc_int *d;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	list_for_each_entry(d, &intc_list, list) {
4398c2ecf20Sopenharmony_ci		int irq;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		if (d->skip_suspend)
4428c2ecf20Sopenharmony_ci			continue;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		for_each_active_irq(irq) {
4458c2ecf20Sopenharmony_ci			struct irq_data *data;
4468c2ecf20Sopenharmony_ci			struct irq_chip *chip;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci			data = irq_get_irq_data(irq);
4498c2ecf20Sopenharmony_ci			chip = irq_data_get_irq_chip(data);
4508c2ecf20Sopenharmony_ci			/*
4518c2ecf20Sopenharmony_ci			 * This will catch the redirect and VIRQ cases
4528c2ecf20Sopenharmony_ci			 * due to the dummy_irq_chip being inserted.
4538c2ecf20Sopenharmony_ci			 */
4548c2ecf20Sopenharmony_ci			if (chip != &d->chip)
4558c2ecf20Sopenharmony_ci				continue;
4568c2ecf20Sopenharmony_ci			if (irqd_irq_disabled(data))
4578c2ecf20Sopenharmony_ci				chip->irq_disable(data);
4588c2ecf20Sopenharmony_ci			else
4598c2ecf20Sopenharmony_ci				chip->irq_enable(data);
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistruct syscore_ops intc_syscore_ops = {
4658c2ecf20Sopenharmony_ci	.suspend	= intc_suspend,
4668c2ecf20Sopenharmony_ci	.resume		= intc_resume,
4678c2ecf20Sopenharmony_ci};
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistruct bus_type intc_subsys = {
4708c2ecf20Sopenharmony_ci	.name		= "intc",
4718c2ecf20Sopenharmony_ci	.dev_name	= "intc",
4728c2ecf20Sopenharmony_ci};
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic ssize_t
4758c2ecf20Sopenharmony_cishow_intc_name(struct device *dev, struct device_attribute *attr, char *buf)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct intc_desc_int *d;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	d = container_of(dev, struct intc_desc_int, dev);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", d->chip.name);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int __init register_intc_devs(void)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct intc_desc_int *d;
4898c2ecf20Sopenharmony_ci	int error;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	register_syscore_ops(&intc_syscore_ops);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	error = subsys_system_register(&intc_subsys, NULL);
4948c2ecf20Sopenharmony_ci	if (!error) {
4958c2ecf20Sopenharmony_ci		list_for_each_entry(d, &intc_list, list) {
4968c2ecf20Sopenharmony_ci			d->dev.id = d->index;
4978c2ecf20Sopenharmony_ci			d->dev.bus = &intc_subsys;
4988c2ecf20Sopenharmony_ci			error = device_register(&d->dev);
4998c2ecf20Sopenharmony_ci			if (error == 0)
5008c2ecf20Sopenharmony_ci				error = device_create_file(&d->dev,
5018c2ecf20Sopenharmony_ci							   &dev_attr_name);
5028c2ecf20Sopenharmony_ci			if (error)
5038c2ecf20Sopenharmony_ci				break;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (error)
5088c2ecf20Sopenharmony_ci		pr_err("device registration error\n");
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return error;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_cidevice_initcall(register_intc_devs);
513