xref: /openbmc/linux/drivers/irqchip/irq-tegra.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2de3ce080SMarc Zyngier /*
3de3ce080SMarc Zyngier  * Driver code for Tegra's Legacy Interrupt Controller
4de3ce080SMarc Zyngier  *
5de3ce080SMarc Zyngier  * Author: Marc Zyngier <marc.zyngier@arm.com>
6de3ce080SMarc Zyngier  *
7de3ce080SMarc Zyngier  * Heavily based on the original arch/arm/mach-tegra/irq.c code:
8de3ce080SMarc Zyngier  * Copyright (C) 2011 Google, Inc.
9de3ce080SMarc Zyngier  *
10de3ce080SMarc Zyngier  * Author:
11de3ce080SMarc Zyngier  *	Colin Cross <ccross@android.com>
12de3ce080SMarc Zyngier  *
13de3ce080SMarc Zyngier  * Copyright (C) 2010,2013, NVIDIA Corporation
14de3ce080SMarc Zyngier  */
15de3ce080SMarc Zyngier 
16de3ce080SMarc Zyngier #include <linux/io.h>
17de3ce080SMarc Zyngier #include <linux/irq.h>
1841a83e06SJoel Porquet #include <linux/irqchip.h>
19de3ce080SMarc Zyngier #include <linux/irqdomain.h>
20de3ce080SMarc Zyngier #include <linux/of_address.h>
21de3ce080SMarc Zyngier #include <linux/slab.h>
22de3ce080SMarc Zyngier #include <linux/syscore_ops.h>
23de3ce080SMarc Zyngier 
24de3ce080SMarc Zyngier #include <dt-bindings/interrupt-controller/arm-gic.h>
25de3ce080SMarc Zyngier 
26de3ce080SMarc Zyngier #define ICTLR_CPU_IEP_VFIQ	0x08
27de3ce080SMarc Zyngier #define ICTLR_CPU_IEP_FIR	0x14
28de3ce080SMarc Zyngier #define ICTLR_CPU_IEP_FIR_SET	0x18
29de3ce080SMarc Zyngier #define ICTLR_CPU_IEP_FIR_CLR	0x1c
30de3ce080SMarc Zyngier 
31de3ce080SMarc Zyngier #define ICTLR_CPU_IER		0x20
32de3ce080SMarc Zyngier #define ICTLR_CPU_IER_SET	0x24
33de3ce080SMarc Zyngier #define ICTLR_CPU_IER_CLR	0x28
34de3ce080SMarc Zyngier #define ICTLR_CPU_IEP_CLASS	0x2C
35de3ce080SMarc Zyngier 
36de3ce080SMarc Zyngier #define ICTLR_COP_IER		0x30
37de3ce080SMarc Zyngier #define ICTLR_COP_IER_SET	0x34
38de3ce080SMarc Zyngier #define ICTLR_COP_IER_CLR	0x38
39de3ce080SMarc Zyngier #define ICTLR_COP_IEP_CLASS	0x3c
40de3ce080SMarc Zyngier 
411eec5821SThierry Reding #define TEGRA_MAX_NUM_ICTLRS	6
42de3ce080SMarc Zyngier 
43de3ce080SMarc Zyngier static unsigned int num_ictlrs;
44de3ce080SMarc Zyngier 
45de3ce080SMarc Zyngier struct tegra_ictlr_soc {
46de3ce080SMarc Zyngier 	unsigned int num_ictlrs;
47de3ce080SMarc Zyngier };
48de3ce080SMarc Zyngier 
49de3ce080SMarc Zyngier static const struct tegra_ictlr_soc tegra20_ictlr_soc = {
50de3ce080SMarc Zyngier 	.num_ictlrs = 4,
51de3ce080SMarc Zyngier };
52de3ce080SMarc Zyngier 
53de3ce080SMarc Zyngier static const struct tegra_ictlr_soc tegra30_ictlr_soc = {
54de3ce080SMarc Zyngier 	.num_ictlrs = 5,
55de3ce080SMarc Zyngier };
56de3ce080SMarc Zyngier 
571eec5821SThierry Reding static const struct tegra_ictlr_soc tegra210_ictlr_soc = {
581eec5821SThierry Reding 	.num_ictlrs = 6,
591eec5821SThierry Reding };
601eec5821SThierry Reding 
61de3ce080SMarc Zyngier static const struct of_device_id ictlr_matches[] = {
621eec5821SThierry Reding 	{ .compatible = "nvidia,tegra210-ictlr", .data = &tegra210_ictlr_soc },
63de3ce080SMarc Zyngier 	{ .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc },
64de3ce080SMarc Zyngier 	{ .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc },
65de3ce080SMarc Zyngier 	{ }
66de3ce080SMarc Zyngier };
67de3ce080SMarc Zyngier 
68de3ce080SMarc Zyngier struct tegra_ictlr_info {
69de3ce080SMarc Zyngier 	void __iomem *base[TEGRA_MAX_NUM_ICTLRS];
70de3ce080SMarc Zyngier #ifdef CONFIG_PM_SLEEP
71de3ce080SMarc Zyngier 	u32 cop_ier[TEGRA_MAX_NUM_ICTLRS];
72de3ce080SMarc Zyngier 	u32 cop_iep[TEGRA_MAX_NUM_ICTLRS];
73de3ce080SMarc Zyngier 	u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];
74de3ce080SMarc Zyngier 	u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS];
75de3ce080SMarc Zyngier 
76de3ce080SMarc Zyngier 	u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS];
77de3ce080SMarc Zyngier #endif
78de3ce080SMarc Zyngier };
79de3ce080SMarc Zyngier 
80de3ce080SMarc Zyngier static struct tegra_ictlr_info *lic;
81de3ce080SMarc Zyngier 
tegra_ictlr_write_mask(struct irq_data * d,unsigned long reg)82de3ce080SMarc Zyngier static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg)
83de3ce080SMarc Zyngier {
84f6fbaaa4SBen Dooks 	void __iomem *base = (void __iomem __force *)d->chip_data;
85de3ce080SMarc Zyngier 	u32 mask;
86de3ce080SMarc Zyngier 
87de3ce080SMarc Zyngier 	mask = BIT(d->hwirq % 32);
88de3ce080SMarc Zyngier 	writel_relaxed(mask, base + reg);
89de3ce080SMarc Zyngier }
90de3ce080SMarc Zyngier 
tegra_mask(struct irq_data * d)91de3ce080SMarc Zyngier static void tegra_mask(struct irq_data *d)
92de3ce080SMarc Zyngier {
93de3ce080SMarc Zyngier 	tegra_ictlr_write_mask(d, ICTLR_CPU_IER_CLR);
94de3ce080SMarc Zyngier 	irq_chip_mask_parent(d);
95de3ce080SMarc Zyngier }
96de3ce080SMarc Zyngier 
tegra_unmask(struct irq_data * d)97de3ce080SMarc Zyngier static void tegra_unmask(struct irq_data *d)
98de3ce080SMarc Zyngier {
99de3ce080SMarc Zyngier 	tegra_ictlr_write_mask(d, ICTLR_CPU_IER_SET);
100de3ce080SMarc Zyngier 	irq_chip_unmask_parent(d);
101de3ce080SMarc Zyngier }
102de3ce080SMarc Zyngier 
tegra_eoi(struct irq_data * d)103de3ce080SMarc Zyngier static void tegra_eoi(struct irq_data *d)
104de3ce080SMarc Zyngier {
105de3ce080SMarc Zyngier 	tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_CLR);
106de3ce080SMarc Zyngier 	irq_chip_eoi_parent(d);
107de3ce080SMarc Zyngier }
108de3ce080SMarc Zyngier 
tegra_retrigger(struct irq_data * d)109de3ce080SMarc Zyngier static int tegra_retrigger(struct irq_data *d)
110de3ce080SMarc Zyngier {
111de3ce080SMarc Zyngier 	tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_SET);
112de3ce080SMarc Zyngier 	return irq_chip_retrigger_hierarchy(d);
113de3ce080SMarc Zyngier }
114de3ce080SMarc Zyngier 
115de3ce080SMarc Zyngier #ifdef CONFIG_PM_SLEEP
tegra_set_wake(struct irq_data * d,unsigned int enable)116de3ce080SMarc Zyngier static int tegra_set_wake(struct irq_data *d, unsigned int enable)
117de3ce080SMarc Zyngier {
118de3ce080SMarc Zyngier 	u32 irq = d->hwirq;
119de3ce080SMarc Zyngier 	u32 index, mask;
120de3ce080SMarc Zyngier 
121de3ce080SMarc Zyngier 	index = (irq / 32);
122de3ce080SMarc Zyngier 	mask = BIT(irq % 32);
123de3ce080SMarc Zyngier 	if (enable)
124de3ce080SMarc Zyngier 		lic->ictlr_wake_mask[index] |= mask;
125de3ce080SMarc Zyngier 	else
126de3ce080SMarc Zyngier 		lic->ictlr_wake_mask[index] &= ~mask;
127de3ce080SMarc Zyngier 
128de3ce080SMarc Zyngier 	/*
129de3ce080SMarc Zyngier 	 * Do *not* call into the parent, as the GIC doesn't have any
130de3ce080SMarc Zyngier 	 * wake-up facility...
131de3ce080SMarc Zyngier 	 */
132de3ce080SMarc Zyngier 	return 0;
133de3ce080SMarc Zyngier }
134de3ce080SMarc Zyngier 
tegra_ictlr_suspend(void)135de3ce080SMarc Zyngier static int tegra_ictlr_suspend(void)
136de3ce080SMarc Zyngier {
137de3ce080SMarc Zyngier 	unsigned long flags;
138de3ce080SMarc Zyngier 	unsigned int i;
139de3ce080SMarc Zyngier 
140de3ce080SMarc Zyngier 	local_irq_save(flags);
141de3ce080SMarc Zyngier 	for (i = 0; i < num_ictlrs; i++) {
142de3ce080SMarc Zyngier 		void __iomem *ictlr = lic->base[i];
143de3ce080SMarc Zyngier 
144de3ce080SMarc Zyngier 		/* Save interrupt state */
145de3ce080SMarc Zyngier 		lic->cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER);
146de3ce080SMarc Zyngier 		lic->cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS);
147de3ce080SMarc Zyngier 		lic->cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER);
148de3ce080SMarc Zyngier 		lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
149de3ce080SMarc Zyngier 
150de3ce080SMarc Zyngier 		/* Disable COP interrupts */
151*44368599SSai Prakash Ranjan 		writel_relaxed(GENMASK(31, 0), ictlr + ICTLR_COP_IER_CLR);
152de3ce080SMarc Zyngier 
153de3ce080SMarc Zyngier 		/* Disable CPU interrupts */
154*44368599SSai Prakash Ranjan 		writel_relaxed(GENMASK(31, 0), ictlr + ICTLR_CPU_IER_CLR);
155de3ce080SMarc Zyngier 
156de3ce080SMarc Zyngier 		/* Enable the wakeup sources of ictlr */
157de3ce080SMarc Zyngier 		writel_relaxed(lic->ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET);
158de3ce080SMarc Zyngier 	}
159de3ce080SMarc Zyngier 	local_irq_restore(flags);
160de3ce080SMarc Zyngier 
161de3ce080SMarc Zyngier 	return 0;
162de3ce080SMarc Zyngier }
163de3ce080SMarc Zyngier 
tegra_ictlr_resume(void)164de3ce080SMarc Zyngier static void tegra_ictlr_resume(void)
165de3ce080SMarc Zyngier {
166de3ce080SMarc Zyngier 	unsigned long flags;
167de3ce080SMarc Zyngier 	unsigned int i;
168de3ce080SMarc Zyngier 
169de3ce080SMarc Zyngier 	local_irq_save(flags);
170de3ce080SMarc Zyngier 	for (i = 0; i < num_ictlrs; i++) {
171de3ce080SMarc Zyngier 		void __iomem *ictlr = lic->base[i];
172de3ce080SMarc Zyngier 
173de3ce080SMarc Zyngier 		writel_relaxed(lic->cpu_iep[i],
174de3ce080SMarc Zyngier 			       ictlr + ICTLR_CPU_IEP_CLASS);
175*44368599SSai Prakash Ranjan 		writel_relaxed(GENMASK(31, 0), ictlr + ICTLR_CPU_IER_CLR);
176de3ce080SMarc Zyngier 		writel_relaxed(lic->cpu_ier[i],
177de3ce080SMarc Zyngier 			       ictlr + ICTLR_CPU_IER_SET);
178de3ce080SMarc Zyngier 		writel_relaxed(lic->cop_iep[i],
179de3ce080SMarc Zyngier 			       ictlr + ICTLR_COP_IEP_CLASS);
180*44368599SSai Prakash Ranjan 		writel_relaxed(GENMASK(31, 0), ictlr + ICTLR_COP_IER_CLR);
181de3ce080SMarc Zyngier 		writel_relaxed(lic->cop_ier[i],
182de3ce080SMarc Zyngier 			       ictlr + ICTLR_COP_IER_SET);
183de3ce080SMarc Zyngier 	}
184de3ce080SMarc Zyngier 	local_irq_restore(flags);
185de3ce080SMarc Zyngier }
186de3ce080SMarc Zyngier 
187de3ce080SMarc Zyngier static struct syscore_ops tegra_ictlr_syscore_ops = {
188de3ce080SMarc Zyngier 	.suspend	= tegra_ictlr_suspend,
189de3ce080SMarc Zyngier 	.resume		= tegra_ictlr_resume,
190de3ce080SMarc Zyngier };
191de3ce080SMarc Zyngier 
tegra_ictlr_syscore_init(void)192de3ce080SMarc Zyngier static void tegra_ictlr_syscore_init(void)
193de3ce080SMarc Zyngier {
194de3ce080SMarc Zyngier 	register_syscore_ops(&tegra_ictlr_syscore_ops);
195de3ce080SMarc Zyngier }
196de3ce080SMarc Zyngier #else
197de3ce080SMarc Zyngier #define tegra_set_wake	NULL
tegra_ictlr_syscore_init(void)198de3ce080SMarc Zyngier static inline void tegra_ictlr_syscore_init(void) {}
199de3ce080SMarc Zyngier #endif
200de3ce080SMarc Zyngier 
201de3ce080SMarc Zyngier static struct irq_chip tegra_ictlr_chip = {
202de3ce080SMarc Zyngier 	.name			= "LIC",
203de3ce080SMarc Zyngier 	.irq_eoi		= tegra_eoi,
204de3ce080SMarc Zyngier 	.irq_mask		= tegra_mask,
205de3ce080SMarc Zyngier 	.irq_unmask		= tegra_unmask,
206de3ce080SMarc Zyngier 	.irq_retrigger		= tegra_retrigger,
207de3ce080SMarc Zyngier 	.irq_set_wake		= tegra_set_wake,
208209da391SLucas Stach 	.irq_set_type		= irq_chip_set_type_parent,
209de3ce080SMarc Zyngier 	.flags			= IRQCHIP_MASK_ON_SUSPEND,
210de3ce080SMarc Zyngier #ifdef CONFIG_SMP
211de3ce080SMarc Zyngier 	.irq_set_affinity	= irq_chip_set_affinity_parent,
212de3ce080SMarc Zyngier #endif
213de3ce080SMarc Zyngier };
214de3ce080SMarc Zyngier 
tegra_ictlr_domain_translate(struct irq_domain * d,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)215f833f57fSMarc Zyngier static int tegra_ictlr_domain_translate(struct irq_domain *d,
216f833f57fSMarc Zyngier 					struct irq_fwspec *fwspec,
217f833f57fSMarc Zyngier 					unsigned long *hwirq,
218f833f57fSMarc Zyngier 					unsigned int *type)
219de3ce080SMarc Zyngier {
220f833f57fSMarc Zyngier 	if (is_of_node(fwspec->fwnode)) {
221f833f57fSMarc Zyngier 		if (fwspec->param_count != 3)
222f833f57fSMarc Zyngier 			return -EINVAL;
223de3ce080SMarc Zyngier 
224f833f57fSMarc Zyngier 		/* No PPI should point to this domain */
225f833f57fSMarc Zyngier 		if (fwspec->param[0] != 0)
226f833f57fSMarc Zyngier 			return -EINVAL;
227f833f57fSMarc Zyngier 
228f833f57fSMarc Zyngier 		*hwirq = fwspec->param[1];
229a2a8fa55SJon Hunter 		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
230de3ce080SMarc Zyngier 		return 0;
231de3ce080SMarc Zyngier 	}
232de3ce080SMarc Zyngier 
233f833f57fSMarc Zyngier 	return -EINVAL;
234f833f57fSMarc Zyngier }
235f833f57fSMarc Zyngier 
tegra_ictlr_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * data)236de3ce080SMarc Zyngier static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
237de3ce080SMarc Zyngier 				    unsigned int virq,
238de3ce080SMarc Zyngier 				    unsigned int nr_irqs, void *data)
239de3ce080SMarc Zyngier {
240f833f57fSMarc Zyngier 	struct irq_fwspec *fwspec = data;
241f833f57fSMarc Zyngier 	struct irq_fwspec parent_fwspec;
242de3ce080SMarc Zyngier 	struct tegra_ictlr_info *info = domain->host_data;
243de3ce080SMarc Zyngier 	irq_hw_number_t hwirq;
244de3ce080SMarc Zyngier 	unsigned int i;
245de3ce080SMarc Zyngier 
246f833f57fSMarc Zyngier 	if (fwspec->param_count != 3)
247de3ce080SMarc Zyngier 		return -EINVAL;	/* Not GIC compliant */
248f833f57fSMarc Zyngier 	if (fwspec->param[0] != GIC_SPI)
249de3ce080SMarc Zyngier 		return -EINVAL;	/* No PPI should point to this domain */
250de3ce080SMarc Zyngier 
251f833f57fSMarc Zyngier 	hwirq = fwspec->param[1];
252de3ce080SMarc Zyngier 	if (hwirq >= (num_ictlrs * 32))
253de3ce080SMarc Zyngier 		return -EINVAL;
254de3ce080SMarc Zyngier 
255de3ce080SMarc Zyngier 	for (i = 0; i < nr_irqs; i++) {
256de3ce080SMarc Zyngier 		int ictlr = (hwirq + i) / 32;
257de3ce080SMarc Zyngier 
258de3ce080SMarc Zyngier 		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
259de3ce080SMarc Zyngier 					      &tegra_ictlr_chip,
260f6fbaaa4SBen Dooks 					      (void __force *)info->base[ictlr]);
261de3ce080SMarc Zyngier 	}
262de3ce080SMarc Zyngier 
263f833f57fSMarc Zyngier 	parent_fwspec = *fwspec;
264f833f57fSMarc Zyngier 	parent_fwspec.fwnode = domain->parent->fwnode;
265f833f57fSMarc Zyngier 	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
266f833f57fSMarc Zyngier 					    &parent_fwspec);
267de3ce080SMarc Zyngier }
268de3ce080SMarc Zyngier 
269de3ce080SMarc Zyngier static const struct irq_domain_ops tegra_ictlr_domain_ops = {
270f833f57fSMarc Zyngier 	.translate	= tegra_ictlr_domain_translate,
271de3ce080SMarc Zyngier 	.alloc		= tegra_ictlr_domain_alloc,
27246f920d8SAxel Lin 	.free		= irq_domain_free_irqs_common,
273de3ce080SMarc Zyngier };
274de3ce080SMarc Zyngier 
tegra_ictlr_init(struct device_node * node,struct device_node * parent)275de3ce080SMarc Zyngier static int __init tegra_ictlr_init(struct device_node *node,
276de3ce080SMarc Zyngier 				   struct device_node *parent)
277de3ce080SMarc Zyngier {
278de3ce080SMarc Zyngier 	struct irq_domain *parent_domain, *domain;
279de3ce080SMarc Zyngier 	const struct of_device_id *match;
280de3ce080SMarc Zyngier 	const struct tegra_ictlr_soc *soc;
281de3ce080SMarc Zyngier 	unsigned int i;
282de3ce080SMarc Zyngier 	int err;
283de3ce080SMarc Zyngier 
284de3ce080SMarc Zyngier 	if (!parent) {
285e81f54c6SRob Herring 		pr_err("%pOF: no parent, giving up\n", node);
286de3ce080SMarc Zyngier 		return -ENODEV;
287de3ce080SMarc Zyngier 	}
288de3ce080SMarc Zyngier 
289de3ce080SMarc Zyngier 	parent_domain = irq_find_host(parent);
290de3ce080SMarc Zyngier 	if (!parent_domain) {
291e81f54c6SRob Herring 		pr_err("%pOF: unable to obtain parent domain\n", node);
292de3ce080SMarc Zyngier 		return -ENXIO;
293de3ce080SMarc Zyngier 	}
294de3ce080SMarc Zyngier 
295de3ce080SMarc Zyngier 	match = of_match_node(ictlr_matches, node);
296de3ce080SMarc Zyngier 	if (!match)		/* Should never happen... */
297de3ce080SMarc Zyngier 		return -ENODEV;
298de3ce080SMarc Zyngier 
299de3ce080SMarc Zyngier 	soc = match->data;
300de3ce080SMarc Zyngier 
301de3ce080SMarc Zyngier 	lic = kzalloc(sizeof(*lic), GFP_KERNEL);
302de3ce080SMarc Zyngier 	if (!lic)
303de3ce080SMarc Zyngier 		return -ENOMEM;
304de3ce080SMarc Zyngier 
305de3ce080SMarc Zyngier 	for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) {
306de3ce080SMarc Zyngier 		void __iomem *base;
307de3ce080SMarc Zyngier 
308de3ce080SMarc Zyngier 		base = of_iomap(node, i);
309de3ce080SMarc Zyngier 		if (!base)
310de3ce080SMarc Zyngier 			break;
311de3ce080SMarc Zyngier 
312de3ce080SMarc Zyngier 		lic->base[i] = base;
313de3ce080SMarc Zyngier 
314de3ce080SMarc Zyngier 		/* Disable all interrupts */
315*44368599SSai Prakash Ranjan 		writel_relaxed(GENMASK(31, 0), base + ICTLR_CPU_IER_CLR);
316de3ce080SMarc Zyngier 		/* All interrupts target IRQ */
317de3ce080SMarc Zyngier 		writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS);
318de3ce080SMarc Zyngier 
319de3ce080SMarc Zyngier 		num_ictlrs++;
320de3ce080SMarc Zyngier 	}
321de3ce080SMarc Zyngier 
322de3ce080SMarc Zyngier 	if (!num_ictlrs) {
323e81f54c6SRob Herring 		pr_err("%pOF: no valid regions, giving up\n", node);
324de3ce080SMarc Zyngier 		err = -ENOMEM;
325de3ce080SMarc Zyngier 		goto out_free;
326de3ce080SMarc Zyngier 	}
327de3ce080SMarc Zyngier 
328de3ce080SMarc Zyngier 	WARN(num_ictlrs != soc->num_ictlrs,
329e81f54c6SRob Herring 	     "%pOF: Found %u interrupt controllers in DT; expected %u.\n",
330e81f54c6SRob Herring 	     node, num_ictlrs, soc->num_ictlrs);
331de3ce080SMarc Zyngier 
332de3ce080SMarc Zyngier 
333de3ce080SMarc Zyngier 	domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32,
334de3ce080SMarc Zyngier 					  node, &tegra_ictlr_domain_ops,
335de3ce080SMarc Zyngier 					  lic);
336de3ce080SMarc Zyngier 	if (!domain) {
337e81f54c6SRob Herring 		pr_err("%pOF: failed to allocated domain\n", node);
338de3ce080SMarc Zyngier 		err = -ENOMEM;
339de3ce080SMarc Zyngier 		goto out_unmap;
340de3ce080SMarc Zyngier 	}
341de3ce080SMarc Zyngier 
342de3ce080SMarc Zyngier 	tegra_ictlr_syscore_init();
343de3ce080SMarc Zyngier 
344e81f54c6SRob Herring 	pr_info("%pOF: %d interrupts forwarded to %pOF\n",
345e81f54c6SRob Herring 		node, num_ictlrs * 32, parent);
346de3ce080SMarc Zyngier 
347de3ce080SMarc Zyngier 	return 0;
348de3ce080SMarc Zyngier 
349de3ce080SMarc Zyngier out_unmap:
350de3ce080SMarc Zyngier 	for (i = 0; i < num_ictlrs; i++)
351de3ce080SMarc Zyngier 		iounmap(lic->base[i]);
352de3ce080SMarc Zyngier out_free:
353de3ce080SMarc Zyngier 	kfree(lic);
354de3ce080SMarc Zyngier 	return err;
355de3ce080SMarc Zyngier }
356de3ce080SMarc Zyngier 
357de3ce080SMarc Zyngier IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init);
358de3ce080SMarc Zyngier IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init);
3591eec5821SThierry Reding IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init);
360