xref: /linux/drivers/irqchip/irq-bcm7120-l2.c (revision 082ce27ff4215a4863d885a48dc443f66f833818)
1a5042de2SFlorian Fainelli /*
2a5042de2SFlorian Fainelli  * Broadcom BCM7120 style Level 2 interrupt controller driver
3a5042de2SFlorian Fainelli  *
4a5042de2SFlorian Fainelli  * Copyright (C) 2014 Broadcom Corporation
5a5042de2SFlorian Fainelli  *
6a5042de2SFlorian Fainelli  * This program is free software; you can redistribute it and/or modify
7a5042de2SFlorian Fainelli  * it under the terms of the GNU General Public License version 2 as
8a5042de2SFlorian Fainelli  * published by the Free Software Foundation.
9a5042de2SFlorian Fainelli  */
10a5042de2SFlorian Fainelli 
11a5042de2SFlorian Fainelli #define pr_fmt(fmt)	KBUILD_MODNAME	": " fmt
12a5042de2SFlorian Fainelli 
13a5042de2SFlorian Fainelli #include <linux/init.h>
14a5042de2SFlorian Fainelli #include <linux/slab.h>
15a5042de2SFlorian Fainelli #include <linux/module.h>
167b7230e7SKevin Cernekee #include <linux/kernel.h>
17a5042de2SFlorian Fainelli #include <linux/platform_device.h>
18a5042de2SFlorian Fainelli #include <linux/of.h>
19a5042de2SFlorian Fainelli #include <linux/of_irq.h>
20a5042de2SFlorian Fainelli #include <linux/of_address.h>
21a5042de2SFlorian Fainelli #include <linux/of_platform.h>
22a5042de2SFlorian Fainelli #include <linux/interrupt.h>
23a5042de2SFlorian Fainelli #include <linux/irq.h>
24a5042de2SFlorian Fainelli #include <linux/io.h>
25a5042de2SFlorian Fainelli #include <linux/irqdomain.h>
26a5042de2SFlorian Fainelli #include <linux/reboot.h>
27c76acf4dSKevin Cernekee #include <linux/bitops.h>
2841a83e06SJoel Porquet #include <linux/irqchip.h>
29a5042de2SFlorian Fainelli #include <linux/irqchip/chained_irq.h>
30a5042de2SFlorian Fainelli 
31a5042de2SFlorian Fainelli /* Register offset in the L2 interrupt controller */
32a5042de2SFlorian Fainelli #define IRQEN		0x00
33a5042de2SFlorian Fainelli #define IRQSTAT		0x04
34a5042de2SFlorian Fainelli 
35c76acf4dSKevin Cernekee #define MAX_WORDS	4
36ca40f1b2SKevin Cernekee #define MAX_MAPPINGS	(MAX_WORDS * 2)
37c76acf4dSKevin Cernekee #define IRQS_PER_WORD	32
38c76acf4dSKevin Cernekee 
390aef3997SFlorian Fainelli struct bcm7120_l1_intc_data {
400aef3997SFlorian Fainelli 	struct bcm7120_l2_intc_data *b;
410aef3997SFlorian Fainelli 	u32 irq_map_mask[MAX_WORDS];
420aef3997SFlorian Fainelli };
430aef3997SFlorian Fainelli 
44a5042de2SFlorian Fainelli struct bcm7120_l2_intc_data {
45c76acf4dSKevin Cernekee 	unsigned int n_words;
465b5468cfSKevin Cernekee 	void __iomem *map_base[MAX_MAPPINGS];
475b5468cfSKevin Cernekee 	void __iomem *pair_base[MAX_WORDS];
485b5468cfSKevin Cernekee 	int en_offset[MAX_WORDS];
495b5468cfSKevin Cernekee 	int stat_offset[MAX_WORDS];
50a5042de2SFlorian Fainelli 	struct irq_domain *domain;
51a5042de2SFlorian Fainelli 	bool can_wake;
52c76acf4dSKevin Cernekee 	u32 irq_fwd_mask[MAX_WORDS];
530aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *l1_data;
54ca40f1b2SKevin Cernekee 	int num_parent_irqs;
55ca40f1b2SKevin Cernekee 	const __be32 *map_mask_prop;
56a5042de2SFlorian Fainelli };
57a5042de2SFlorian Fainelli 
58bd0b9ac4SThomas Gleixner static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc)
59a5042de2SFlorian Fainelli {
600aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc);
610aef3997SFlorian Fainelli 	struct bcm7120_l2_intc_data *b = data->b;
62a5042de2SFlorian Fainelli 	struct irq_chip *chip = irq_desc_get_chip(desc);
63c76acf4dSKevin Cernekee 	unsigned int idx;
64a5042de2SFlorian Fainelli 
65a5042de2SFlorian Fainelli 	chained_irq_enter(chip, desc);
66a5042de2SFlorian Fainelli 
67c76acf4dSKevin Cernekee 	for (idx = 0; idx < b->n_words; idx++) {
68c76acf4dSKevin Cernekee 		int base = idx * IRQS_PER_WORD;
69c76acf4dSKevin Cernekee 		struct irq_chip_generic *gc =
70c76acf4dSKevin Cernekee 			irq_get_domain_generic_chip(b->domain, base);
71c76acf4dSKevin Cernekee 		unsigned long pending;
72c76acf4dSKevin Cernekee 		int hwirq;
73a5042de2SFlorian Fainelli 
74c76acf4dSKevin Cernekee 		irq_gc_lock(gc);
755b5468cfSKevin Cernekee 		pending = irq_reg_readl(gc, b->stat_offset[idx]) &
760aef3997SFlorian Fainelli 					    gc->mask_cache &
770aef3997SFlorian Fainelli 					    data->irq_map_mask[idx];
78c76acf4dSKevin Cernekee 		irq_gc_unlock(gc);
79c76acf4dSKevin Cernekee 
80c76acf4dSKevin Cernekee 		for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
81c76acf4dSKevin Cernekee 			generic_handle_irq(irq_find_mapping(b->domain,
82c76acf4dSKevin Cernekee 					   base + hwirq));
83c76acf4dSKevin Cernekee 		}
84a5042de2SFlorian Fainelli 	}
85a5042de2SFlorian Fainelli 
86a5042de2SFlorian Fainelli 	chained_irq_exit(chip, desc);
87a5042de2SFlorian Fainelli }
88a5042de2SFlorian Fainelli 
89fd537766SBrian Norris static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc)
90a5042de2SFlorian Fainelli {
91a5042de2SFlorian Fainelli 	struct bcm7120_l2_intc_data *b = gc->private;
92fd537766SBrian Norris 	struct irq_chip_type *ct = gc->chip_types;
93a5042de2SFlorian Fainelli 
94a5042de2SFlorian Fainelli 	irq_gc_lock(gc);
95c17261faSKevin Cernekee 	if (b->can_wake)
965b5468cfSKevin Cernekee 		irq_reg_writel(gc, gc->mask_cache | gc->wake_active,
975b5468cfSKevin Cernekee 			       ct->regs.mask);
98a5042de2SFlorian Fainelli 	irq_gc_unlock(gc);
99a5042de2SFlorian Fainelli }
100a5042de2SFlorian Fainelli 
101fd537766SBrian Norris static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc)
102a5042de2SFlorian Fainelli {
103fd537766SBrian Norris 	struct irq_chip_type *ct = gc->chip_types;
104a5042de2SFlorian Fainelli 
105a5042de2SFlorian Fainelli 	/* Restore the saved mask */
106a5042de2SFlorian Fainelli 	irq_gc_lock(gc);
1075b5468cfSKevin Cernekee 	irq_reg_writel(gc, gc->mask_cache, ct->regs.mask);
108a5042de2SFlorian Fainelli 	irq_gc_unlock(gc);
109a5042de2SFlorian Fainelli }
110a5042de2SFlorian Fainelli 
111a5042de2SFlorian Fainelli static int bcm7120_l2_intc_init_one(struct device_node *dn,
112a5042de2SFlorian Fainelli 					struct bcm7120_l2_intc_data *data,
1130aef3997SFlorian Fainelli 					int irq, u32 *valid_mask)
114a5042de2SFlorian Fainelli {
1150aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq];
116a5042de2SFlorian Fainelli 	int parent_irq;
117c76acf4dSKevin Cernekee 	unsigned int idx;
118a5042de2SFlorian Fainelli 
119a5042de2SFlorian Fainelli 	parent_irq = irq_of_parse_and_map(dn, irq);
120714710e1SDmitry Torokhov 	if (!parent_irq) {
121a5042de2SFlorian Fainelli 		pr_err("failed to map interrupt %d\n", irq);
122714710e1SDmitry Torokhov 		return -EINVAL;
123a5042de2SFlorian Fainelli 	}
124a5042de2SFlorian Fainelli 
125c76acf4dSKevin Cernekee 	/* For multiple parent IRQs with multiple words, this looks like:
126c76acf4dSKevin Cernekee 	 * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
1270aef3997SFlorian Fainelli 	 *
1280aef3997SFlorian Fainelli 	 * We need to associate a given parent interrupt with its corresponding
1290aef3997SFlorian Fainelli 	 * map_mask in order to mask the status register with it because we
1300aef3997SFlorian Fainelli 	 * have the same handler being called for multiple parent interrupts.
1310aef3997SFlorian Fainelli 	 *
1320aef3997SFlorian Fainelli 	 * This is typically something needed on BCM7xxx (STB chips).
133c76acf4dSKevin Cernekee 	 */
1347b7230e7SKevin Cernekee 	for (idx = 0; idx < data->n_words; idx++) {
1357b7230e7SKevin Cernekee 		if (data->map_mask_prop) {
1360aef3997SFlorian Fainelli 			l1_data->irq_map_mask[idx] |=
137ca40f1b2SKevin Cernekee 				be32_to_cpup(data->map_mask_prop +
138ca40f1b2SKevin Cernekee 					     irq * data->n_words + idx);
1397b7230e7SKevin Cernekee 		} else {
1400aef3997SFlorian Fainelli 			l1_data->irq_map_mask[idx] = 0xffffffff;
1417b7230e7SKevin Cernekee 		}
1420aef3997SFlorian Fainelli 		valid_mask[idx] |= l1_data->irq_map_mask[idx];
1437b7230e7SKevin Cernekee 	}
144a5042de2SFlorian Fainelli 
1450aef3997SFlorian Fainelli 	l1_data->b = data;
1460aef3997SFlorian Fainelli 
14799e32ab1SThomas Gleixner 	irq_set_chained_handler_and_data(parent_irq,
1480aef3997SFlorian Fainelli 					 bcm7120_l2_intc_irq_handle, l1_data);
149a5042de2SFlorian Fainelli 	return 0;
150a5042de2SFlorian Fainelli }
151a5042de2SFlorian Fainelli 
152ca40f1b2SKevin Cernekee static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn,
153ca40f1b2SKevin Cernekee 					     struct bcm7120_l2_intc_data *data)
154ca40f1b2SKevin Cernekee {
155ca40f1b2SKevin Cernekee 	int ret;
156ca40f1b2SKevin Cernekee 
157ca40f1b2SKevin Cernekee 	data->map_base[0] = of_iomap(dn, 0);
158ca40f1b2SKevin Cernekee 	if (!data->map_base[0]) {
159ca40f1b2SKevin Cernekee 		pr_err("unable to map registers\n");
160ca40f1b2SKevin Cernekee 		return -ENOMEM;
161ca40f1b2SKevin Cernekee 	}
162ca40f1b2SKevin Cernekee 
163ca40f1b2SKevin Cernekee 	data->pair_base[0] = data->map_base[0];
164ca40f1b2SKevin Cernekee 	data->en_offset[0] = IRQEN;
165ca40f1b2SKevin Cernekee 	data->stat_offset[0] = IRQSTAT;
166ca40f1b2SKevin Cernekee 	data->n_words = 1;
167ca40f1b2SKevin Cernekee 
168ca40f1b2SKevin Cernekee 	ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask",
169ca40f1b2SKevin Cernekee 					 data->irq_fwd_mask, data->n_words);
170ca40f1b2SKevin Cernekee 	if (ret != 0 && ret != -EINVAL) {
171ca40f1b2SKevin Cernekee 		/* property exists but has the wrong number of words */
172ca40f1b2SKevin Cernekee 		pr_err("invalid brcm,int-fwd-mask property\n");
173ca40f1b2SKevin Cernekee 		return -EINVAL;
174ca40f1b2SKevin Cernekee 	}
175ca40f1b2SKevin Cernekee 
176ca40f1b2SKevin Cernekee 	data->map_mask_prop = of_get_property(dn, "brcm,int-map-mask", &ret);
177ca40f1b2SKevin Cernekee 	if (!data->map_mask_prop ||
178ca40f1b2SKevin Cernekee 	    (ret != (sizeof(__be32) * data->num_parent_irqs * data->n_words))) {
179ca40f1b2SKevin Cernekee 		pr_err("invalid brcm,int-map-mask property\n");
180ca40f1b2SKevin Cernekee 		return -EINVAL;
181ca40f1b2SKevin Cernekee 	}
182ca40f1b2SKevin Cernekee 
183ca40f1b2SKevin Cernekee 	return 0;
184ca40f1b2SKevin Cernekee }
185ca40f1b2SKevin Cernekee 
1867b7230e7SKevin Cernekee static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
1877b7230e7SKevin Cernekee 					     struct bcm7120_l2_intc_data *data)
1887b7230e7SKevin Cernekee {
1897b7230e7SKevin Cernekee 	unsigned int gc_idx;
1907b7230e7SKevin Cernekee 
1917b7230e7SKevin Cernekee 	for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) {
1927b7230e7SKevin Cernekee 		unsigned int map_idx = gc_idx * 2;
1937b7230e7SKevin Cernekee 		void __iomem *en = of_iomap(dn, map_idx + 0);
1947b7230e7SKevin Cernekee 		void __iomem *stat = of_iomap(dn, map_idx + 1);
1957b7230e7SKevin Cernekee 		void __iomem *base = min(en, stat);
1967b7230e7SKevin Cernekee 
1977b7230e7SKevin Cernekee 		data->map_base[map_idx + 0] = en;
1987b7230e7SKevin Cernekee 		data->map_base[map_idx + 1] = stat;
1997b7230e7SKevin Cernekee 
2007b7230e7SKevin Cernekee 		if (!base)
2017b7230e7SKevin Cernekee 			break;
2027b7230e7SKevin Cernekee 
2037b7230e7SKevin Cernekee 		data->pair_base[gc_idx] = base;
2047b7230e7SKevin Cernekee 		data->en_offset[gc_idx] = en - base;
2057b7230e7SKevin Cernekee 		data->stat_offset[gc_idx] = stat - base;
2067b7230e7SKevin Cernekee 	}
2077b7230e7SKevin Cernekee 
2087b7230e7SKevin Cernekee 	if (!gc_idx) {
2097b7230e7SKevin Cernekee 		pr_err("unable to map registers\n");
2107b7230e7SKevin Cernekee 		return -EINVAL;
2117b7230e7SKevin Cernekee 	}
2127b7230e7SKevin Cernekee 
2137b7230e7SKevin Cernekee 	data->n_words = gc_idx;
2147b7230e7SKevin Cernekee 	return 0;
2157b7230e7SKevin Cernekee }
2167b7230e7SKevin Cernekee 
217dde7e6d1SBen Dooks static int __init bcm7120_l2_intc_probe(struct device_node *dn,
218ca40f1b2SKevin Cernekee 				 struct device_node *parent,
219ca40f1b2SKevin Cernekee 				 int (*iomap_regs_fn)(struct device_node *,
220ca40f1b2SKevin Cernekee 					struct bcm7120_l2_intc_data *),
221ca40f1b2SKevin Cernekee 				 const char *intc_name)
222a5042de2SFlorian Fainelli {
223a5042de2SFlorian Fainelli 	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
224a5042de2SFlorian Fainelli 	struct bcm7120_l2_intc_data *data;
225a5042de2SFlorian Fainelli 	struct irq_chip_generic *gc;
226a5042de2SFlorian Fainelli 	struct irq_chip_type *ct;
227ca40f1b2SKevin Cernekee 	int ret = 0;
228c17261faSKevin Cernekee 	unsigned int idx, irq, flags;
2290aef3997SFlorian Fainelli 	u32 valid_mask[MAX_WORDS] = { };
230a5042de2SFlorian Fainelli 
231a5042de2SFlorian Fainelli 	data = kzalloc(sizeof(*data), GFP_KERNEL);
232a5042de2SFlorian Fainelli 	if (!data)
233a5042de2SFlorian Fainelli 		return -ENOMEM;
234a5042de2SFlorian Fainelli 
235ca40f1b2SKevin Cernekee 	data->num_parent_irqs = of_irq_count(dn);
236ca40f1b2SKevin Cernekee 	if (data->num_parent_irqs <= 0) {
237a5042de2SFlorian Fainelli 		pr_err("invalid number of parent interrupts\n");
238a5042de2SFlorian Fainelli 		ret = -ENOMEM;
239a5042de2SFlorian Fainelli 		goto out_unmap;
240a5042de2SFlorian Fainelli 	}
241a5042de2SFlorian Fainelli 
2420aef3997SFlorian Fainelli 	data->l1_data = kcalloc(data->num_parent_irqs, sizeof(*data->l1_data),
2430aef3997SFlorian Fainelli 				GFP_KERNEL);
2440aef3997SFlorian Fainelli 	if (!data->l1_data) {
2450aef3997SFlorian Fainelli 		ret = -ENOMEM;
2460aef3997SFlorian Fainelli 		goto out_free_l1_data;
2470aef3997SFlorian Fainelli 	}
2480aef3997SFlorian Fainelli 
249ca40f1b2SKevin Cernekee 	ret = iomap_regs_fn(dn, data);
250ca40f1b2SKevin Cernekee 	if (ret < 0)
2510aef3997SFlorian Fainelli 		goto out_free_l1_data;
252ca40f1b2SKevin Cernekee 
253ca40f1b2SKevin Cernekee 	for (irq = 0; irq < data->num_parent_irqs; irq++) {
2540aef3997SFlorian Fainelli 		ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask);
255a5042de2SFlorian Fainelli 		if (ret)
2560aef3997SFlorian Fainelli 			goto out_free_l1_data;
257a5042de2SFlorian Fainelli 	}
258a5042de2SFlorian Fainelli 
259c76acf4dSKevin Cernekee 	data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words,
260a5042de2SFlorian Fainelli 					     &irq_generic_chip_ops, NULL);
261a5042de2SFlorian Fainelli 	if (!data->domain) {
262a5042de2SFlorian Fainelli 		ret = -ENOMEM;
2630aef3997SFlorian Fainelli 		goto out_free_l1_data;
264a5042de2SFlorian Fainelli 	}
265a5042de2SFlorian Fainelli 
266c17261faSKevin Cernekee 	/* MIPS chips strapped for BE will automagically configure the
267c17261faSKevin Cernekee 	 * peripheral registers for CPU-native byte order.
268c17261faSKevin Cernekee 	 */
269c17261faSKevin Cernekee 	flags = IRQ_GC_INIT_MASK_CACHE;
270c17261faSKevin Cernekee 	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
271c17261faSKevin Cernekee 		flags |= IRQ_GC_BE_IO;
272c17261faSKevin Cernekee 
273c76acf4dSKevin Cernekee 	ret = irq_alloc_domain_generic_chips(data->domain, IRQS_PER_WORD, 1,
274c17261faSKevin Cernekee 				dn->full_name, handle_level_irq, clr, 0, flags);
275a5042de2SFlorian Fainelli 	if (ret) {
276a5042de2SFlorian Fainelli 		pr_err("failed to allocate generic irq chip\n");
277a5042de2SFlorian Fainelli 		goto out_free_domain;
278a5042de2SFlorian Fainelli 	}
279a5042de2SFlorian Fainelli 
280c76acf4dSKevin Cernekee 	if (of_property_read_bool(dn, "brcm,irq-can-wake"))
281c76acf4dSKevin Cernekee 		data->can_wake = true;
282c76acf4dSKevin Cernekee 
283c76acf4dSKevin Cernekee 	for (idx = 0; idx < data->n_words; idx++) {
284c76acf4dSKevin Cernekee 		irq = idx * IRQS_PER_WORD;
285c76acf4dSKevin Cernekee 		gc = irq_get_domain_generic_chip(data->domain, irq);
286c76acf4dSKevin Cernekee 
2870aef3997SFlorian Fainelli 		gc->unused = 0xffffffff & ~valid_mask[idx];
288a5042de2SFlorian Fainelli 		gc->private = data;
289a5042de2SFlorian Fainelli 		ct = gc->chip_types;
290a5042de2SFlorian Fainelli 
2915b5468cfSKevin Cernekee 		gc->reg_base = data->pair_base[idx];
2925b5468cfSKevin Cernekee 		ct->regs.mask = data->en_offset[idx];
2935b5468cfSKevin Cernekee 
294b304605fSFlorian Fainelli 		/* gc->reg_base is defined and so is gc->writel */
295b304605fSFlorian Fainelli 		irq_reg_writel(gc, data->irq_fwd_mask[idx],
296b304605fSFlorian Fainelli 			       data->en_offset[idx]);
297b304605fSFlorian Fainelli 
298a5042de2SFlorian Fainelli 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
299a5042de2SFlorian Fainelli 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
300a5042de2SFlorian Fainelli 		ct->chip.irq_ack = irq_gc_noop;
301fd537766SBrian Norris 		gc->suspend = bcm7120_l2_intc_suspend;
302fd537766SBrian Norris 		gc->resume = bcm7120_l2_intc_resume;
303fd537766SBrian Norris 
304fd537766SBrian Norris 		/*
305fd537766SBrian Norris 		 * Initialize mask-cache, in case we need it for
306fd537766SBrian Norris 		 * saving/restoring fwd mask even w/o any child interrupts
307fd537766SBrian Norris 		 * installed
308fd537766SBrian Norris 		 */
309fd537766SBrian Norris 		gc->mask_cache = irq_reg_readl(gc, ct->regs.mask);
310a5042de2SFlorian Fainelli 
311c76acf4dSKevin Cernekee 		if (data->can_wake) {
312c76acf4dSKevin Cernekee 			/* This IRQ chip can wake the system, set all
313c76acf4dSKevin Cernekee 			 * relevant child interupts in wake_enabled mask
314a5042de2SFlorian Fainelli 			 */
315a5042de2SFlorian Fainelli 			gc->wake_enabled = 0xffffffff;
316a5042de2SFlorian Fainelli 			gc->wake_enabled &= ~gc->unused;
317a5042de2SFlorian Fainelli 			ct->chip.irq_set_wake = irq_gc_set_wake;
318a5042de2SFlorian Fainelli 		}
319c76acf4dSKevin Cernekee 	}
320a5042de2SFlorian Fainelli 
321*082ce27fSFlorian Fainelli 	pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n",
322*082ce27fSFlorian Fainelli 		intc_name, dn, data->num_parent_irqs);
323*082ce27fSFlorian Fainelli 
324a5042de2SFlorian Fainelli 	return 0;
325a5042de2SFlorian Fainelli 
326a5042de2SFlorian Fainelli out_free_domain:
327a5042de2SFlorian Fainelli 	irq_domain_remove(data->domain);
3280aef3997SFlorian Fainelli out_free_l1_data:
3290aef3997SFlorian Fainelli 	kfree(data->l1_data);
330a5042de2SFlorian Fainelli out_unmap:
3315b5468cfSKevin Cernekee 	for (idx = 0; idx < MAX_MAPPINGS; idx++) {
3325b5468cfSKevin Cernekee 		if (data->map_base[idx])
3335b5468cfSKevin Cernekee 			iounmap(data->map_base[idx]);
334c76acf4dSKevin Cernekee 	}
335a5042de2SFlorian Fainelli 	kfree(data);
336a5042de2SFlorian Fainelli 	return ret;
337a5042de2SFlorian Fainelli }
338ca40f1b2SKevin Cernekee 
339dde7e6d1SBen Dooks static int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
340ca40f1b2SKevin Cernekee 					     struct device_node *parent)
341ca40f1b2SKevin Cernekee {
342ca40f1b2SKevin Cernekee 	return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
343ca40f1b2SKevin Cernekee 				     "BCM7120 L2");
344ca40f1b2SKevin Cernekee }
345ca40f1b2SKevin Cernekee 
346dde7e6d1SBen Dooks static int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
3477b7230e7SKevin Cernekee 					     struct device_node *parent)
3487b7230e7SKevin Cernekee {
3497b7230e7SKevin Cernekee 	return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
3507b7230e7SKevin Cernekee 				     "BCM3380 L2");
3517b7230e7SKevin Cernekee }
3527b7230e7SKevin Cernekee 
353a4fcbb86SKevin Cernekee IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc",
354ca40f1b2SKevin Cernekee 		bcm7120_l2_intc_probe_7120);
3557b7230e7SKevin Cernekee 
3567b7230e7SKevin Cernekee IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc",
3577b7230e7SKevin Cernekee 		bcm7120_l2_intc_probe_3380);
358