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