xref: /linux/drivers/irqchip/irq-bcm7120-l2.c (revision 0ea8a56de21be24cb79abb03dee79aabcd60a316)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a5042de2SFlorian Fainelli /*
3a5042de2SFlorian Fainelli  * Broadcom BCM7120 style Level 2 interrupt controller driver
4a5042de2SFlorian Fainelli  *
5a5042de2SFlorian Fainelli  * Copyright (C) 2014 Broadcom Corporation
6a5042de2SFlorian Fainelli  */
7a5042de2SFlorian Fainelli 
8a5042de2SFlorian Fainelli #define pr_fmt(fmt)	KBUILD_MODNAME	": " fmt
9a5042de2SFlorian Fainelli 
10a5042de2SFlorian Fainelli #include <linux/init.h>
11a5042de2SFlorian Fainelli #include <linux/slab.h>
12a5042de2SFlorian Fainelli #include <linux/module.h>
137b7230e7SKevin Cernekee #include <linux/kernel.h>
14a5042de2SFlorian Fainelli #include <linux/platform_device.h>
15a5042de2SFlorian Fainelli #include <linux/of.h>
16a5042de2SFlorian Fainelli #include <linux/of_irq.h>
17a5042de2SFlorian Fainelli #include <linux/of_address.h>
18a5042de2SFlorian Fainelli #include <linux/of_platform.h>
19a5042de2SFlorian Fainelli #include <linux/interrupt.h>
20a5042de2SFlorian Fainelli #include <linux/irq.h>
21a5042de2SFlorian Fainelli #include <linux/io.h>
22a5042de2SFlorian Fainelli #include <linux/irqdomain.h>
23a5042de2SFlorian Fainelli #include <linux/reboot.h>
24c76acf4dSKevin Cernekee #include <linux/bitops.h>
2541a83e06SJoel Porquet #include <linux/irqchip.h>
26a5042de2SFlorian Fainelli #include <linux/irqchip/chained_irq.h>
27a5042de2SFlorian Fainelli 
28a5042de2SFlorian Fainelli /* Register offset in the L2 interrupt controller */
29a5042de2SFlorian Fainelli #define IRQEN		0x00
30a5042de2SFlorian Fainelli #define IRQSTAT		0x04
31a5042de2SFlorian Fainelli 
32c76acf4dSKevin Cernekee #define MAX_WORDS	4
33ca40f1b2SKevin Cernekee #define MAX_MAPPINGS	(MAX_WORDS * 2)
34c76acf4dSKevin Cernekee #define IRQS_PER_WORD	32
35c76acf4dSKevin Cernekee 
360aef3997SFlorian Fainelli struct bcm7120_l1_intc_data {
370aef3997SFlorian Fainelli 	struct bcm7120_l2_intc_data *b;
380aef3997SFlorian Fainelli 	u32 irq_map_mask[MAX_WORDS];
390aef3997SFlorian Fainelli };
400aef3997SFlorian Fainelli 
41a5042de2SFlorian Fainelli struct bcm7120_l2_intc_data {
42c76acf4dSKevin Cernekee 	unsigned int n_words;
435b5468cfSKevin Cernekee 	void __iomem *map_base[MAX_MAPPINGS];
445b5468cfSKevin Cernekee 	void __iomem *pair_base[MAX_WORDS];
455b5468cfSKevin Cernekee 	int en_offset[MAX_WORDS];
465b5468cfSKevin Cernekee 	int stat_offset[MAX_WORDS];
47a5042de2SFlorian Fainelli 	struct irq_domain *domain;
48a5042de2SFlorian Fainelli 	bool can_wake;
49c76acf4dSKevin Cernekee 	u32 irq_fwd_mask[MAX_WORDS];
500aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *l1_data;
51ca40f1b2SKevin Cernekee 	int num_parent_irqs;
52ca40f1b2SKevin Cernekee 	const __be32 *map_mask_prop;
53a5042de2SFlorian Fainelli };
54a5042de2SFlorian Fainelli 
55bd0b9ac4SThomas Gleixner static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc)
56a5042de2SFlorian Fainelli {
570aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc);
580aef3997SFlorian Fainelli 	struct bcm7120_l2_intc_data *b = data->b;
59a5042de2SFlorian Fainelli 	struct irq_chip *chip = irq_desc_get_chip(desc);
60c76acf4dSKevin Cernekee 	unsigned int idx;
61a5042de2SFlorian Fainelli 
62a5042de2SFlorian Fainelli 	chained_irq_enter(chip, desc);
63a5042de2SFlorian Fainelli 
64c76acf4dSKevin Cernekee 	for (idx = 0; idx < b->n_words; idx++) {
65c76acf4dSKevin Cernekee 		int base = idx * IRQS_PER_WORD;
66c76acf4dSKevin Cernekee 		struct irq_chip_generic *gc =
67c76acf4dSKevin Cernekee 			irq_get_domain_generic_chip(b->domain, base);
68c76acf4dSKevin Cernekee 		unsigned long pending;
69c76acf4dSKevin Cernekee 		int hwirq;
70a5042de2SFlorian Fainelli 
71c76acf4dSKevin Cernekee 		irq_gc_lock(gc);
725b5468cfSKevin Cernekee 		pending = irq_reg_readl(gc, b->stat_offset[idx]) &
730aef3997SFlorian Fainelli 					    gc->mask_cache &
740aef3997SFlorian Fainelli 					    data->irq_map_mask[idx];
75c76acf4dSKevin Cernekee 		irq_gc_unlock(gc);
76c76acf4dSKevin Cernekee 
77c76acf4dSKevin Cernekee 		for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
78c76acf4dSKevin Cernekee 			generic_handle_irq(irq_find_mapping(b->domain,
79c76acf4dSKevin Cernekee 					   base + hwirq));
80c76acf4dSKevin Cernekee 		}
81a5042de2SFlorian Fainelli 	}
82a5042de2SFlorian Fainelli 
83a5042de2SFlorian Fainelli 	chained_irq_exit(chip, desc);
84a5042de2SFlorian Fainelli }
85a5042de2SFlorian Fainelli 
86fd537766SBrian Norris static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc)
87a5042de2SFlorian Fainelli {
88a5042de2SFlorian Fainelli 	struct bcm7120_l2_intc_data *b = gc->private;
89fd537766SBrian Norris 	struct irq_chip_type *ct = gc->chip_types;
90a5042de2SFlorian Fainelli 
91a5042de2SFlorian Fainelli 	irq_gc_lock(gc);
92c17261faSKevin Cernekee 	if (b->can_wake)
935b5468cfSKevin Cernekee 		irq_reg_writel(gc, gc->mask_cache | gc->wake_active,
945b5468cfSKevin Cernekee 			       ct->regs.mask);
95a5042de2SFlorian Fainelli 	irq_gc_unlock(gc);
96a5042de2SFlorian Fainelli }
97a5042de2SFlorian Fainelli 
98fd537766SBrian Norris static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc)
99a5042de2SFlorian Fainelli {
100fd537766SBrian Norris 	struct irq_chip_type *ct = gc->chip_types;
101a5042de2SFlorian Fainelli 
102a5042de2SFlorian Fainelli 	/* Restore the saved mask */
103a5042de2SFlorian Fainelli 	irq_gc_lock(gc);
1045b5468cfSKevin Cernekee 	irq_reg_writel(gc, gc->mask_cache, ct->regs.mask);
105a5042de2SFlorian Fainelli 	irq_gc_unlock(gc);
106a5042de2SFlorian Fainelli }
107a5042de2SFlorian Fainelli 
108a5042de2SFlorian Fainelli static int bcm7120_l2_intc_init_one(struct device_node *dn,
109a5042de2SFlorian Fainelli 					struct bcm7120_l2_intc_data *data,
1100aef3997SFlorian Fainelli 					int irq, u32 *valid_mask)
111a5042de2SFlorian Fainelli {
1120aef3997SFlorian Fainelli 	struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq];
113a5042de2SFlorian Fainelli 	int parent_irq;
114c76acf4dSKevin Cernekee 	unsigned int idx;
115a5042de2SFlorian Fainelli 
116a5042de2SFlorian Fainelli 	parent_irq = irq_of_parse_and_map(dn, irq);
117714710e1SDmitry Torokhov 	if (!parent_irq) {
118a5042de2SFlorian Fainelli 		pr_err("failed to map interrupt %d\n", irq);
119714710e1SDmitry Torokhov 		return -EINVAL;
120a5042de2SFlorian Fainelli 	}
121a5042de2SFlorian Fainelli 
122c76acf4dSKevin Cernekee 	/* For multiple parent IRQs with multiple words, this looks like:
123c76acf4dSKevin Cernekee 	 * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
1240aef3997SFlorian Fainelli 	 *
1250aef3997SFlorian Fainelli 	 * We need to associate a given parent interrupt with its corresponding
1260aef3997SFlorian Fainelli 	 * map_mask in order to mask the status register with it because we
1270aef3997SFlorian Fainelli 	 * have the same handler being called for multiple parent interrupts.
1280aef3997SFlorian Fainelli 	 *
1290aef3997SFlorian Fainelli 	 * This is typically something needed on BCM7xxx (STB chips).
130c76acf4dSKevin Cernekee 	 */
1317b7230e7SKevin Cernekee 	for (idx = 0; idx < data->n_words; idx++) {
1327b7230e7SKevin Cernekee 		if (data->map_mask_prop) {
1330aef3997SFlorian Fainelli 			l1_data->irq_map_mask[idx] |=
134ca40f1b2SKevin Cernekee 				be32_to_cpup(data->map_mask_prop +
135ca40f1b2SKevin Cernekee 					     irq * data->n_words + idx);
1367b7230e7SKevin Cernekee 		} else {
1370aef3997SFlorian Fainelli 			l1_data->irq_map_mask[idx] = 0xffffffff;
1387b7230e7SKevin Cernekee 		}
1390aef3997SFlorian Fainelli 		valid_mask[idx] |= l1_data->irq_map_mask[idx];
1407b7230e7SKevin Cernekee 	}
141a5042de2SFlorian Fainelli 
1420aef3997SFlorian Fainelli 	l1_data->b = data;
1430aef3997SFlorian Fainelli 
14499e32ab1SThomas Gleixner 	irq_set_chained_handler_and_data(parent_irq,
1450aef3997SFlorian Fainelli 					 bcm7120_l2_intc_irq_handle, l1_data);
146*f4ccb745SJustin Chen 	if (data->can_wake)
147*f4ccb745SJustin Chen 		enable_irq_wake(parent_irq);
148*f4ccb745SJustin Chen 
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 
253*f4ccb745SJustin Chen 	data->can_wake = of_property_read_bool(dn, "brcm,irq-can-wake");
254*f4ccb745SJustin Chen 
255ca40f1b2SKevin Cernekee 	for (irq = 0; irq < data->num_parent_irqs; irq++) {
2560aef3997SFlorian Fainelli 		ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask);
257a5042de2SFlorian Fainelli 		if (ret)
2580aef3997SFlorian Fainelli 			goto out_free_l1_data;
259a5042de2SFlorian Fainelli 	}
260a5042de2SFlorian Fainelli 
261c76acf4dSKevin Cernekee 	data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words,
262a5042de2SFlorian Fainelli 					     &irq_generic_chip_ops, NULL);
263a5042de2SFlorian Fainelli 	if (!data->domain) {
264a5042de2SFlorian Fainelli 		ret = -ENOMEM;
2650aef3997SFlorian Fainelli 		goto out_free_l1_data;
266a5042de2SFlorian Fainelli 	}
267a5042de2SFlorian Fainelli 
268c17261faSKevin Cernekee 	/* MIPS chips strapped for BE will automagically configure the
269c17261faSKevin Cernekee 	 * peripheral registers for CPU-native byte order.
270c17261faSKevin Cernekee 	 */
271c17261faSKevin Cernekee 	flags = IRQ_GC_INIT_MASK_CACHE;
272c17261faSKevin Cernekee 	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
273c17261faSKevin Cernekee 		flags |= IRQ_GC_BE_IO;
274c17261faSKevin Cernekee 
275c76acf4dSKevin Cernekee 	ret = irq_alloc_domain_generic_chips(data->domain, IRQS_PER_WORD, 1,
276c17261faSKevin Cernekee 				dn->full_name, handle_level_irq, clr, 0, flags);
277a5042de2SFlorian Fainelli 	if (ret) {
278a5042de2SFlorian Fainelli 		pr_err("failed to allocate generic irq chip\n");
279a5042de2SFlorian Fainelli 		goto out_free_domain;
280a5042de2SFlorian Fainelli 	}
281a5042de2SFlorian Fainelli 
282c76acf4dSKevin Cernekee 	for (idx = 0; idx < data->n_words; idx++) {
283c76acf4dSKevin Cernekee 		irq = idx * IRQS_PER_WORD;
284c76acf4dSKevin Cernekee 		gc = irq_get_domain_generic_chip(data->domain, irq);
285c76acf4dSKevin Cernekee 
2860aef3997SFlorian Fainelli 		gc->unused = 0xffffffff & ~valid_mask[idx];
287a5042de2SFlorian Fainelli 		gc->private = data;
288a5042de2SFlorian Fainelli 		ct = gc->chip_types;
289a5042de2SFlorian Fainelli 
2905b5468cfSKevin Cernekee 		gc->reg_base = data->pair_base[idx];
2915b5468cfSKevin Cernekee 		ct->regs.mask = data->en_offset[idx];
2925b5468cfSKevin Cernekee 
293b304605fSFlorian Fainelli 		/* gc->reg_base is defined and so is gc->writel */
294b304605fSFlorian Fainelli 		irq_reg_writel(gc, data->irq_fwd_mask[idx],
295b304605fSFlorian Fainelli 			       data->en_offset[idx]);
296b304605fSFlorian Fainelli 
297a5042de2SFlorian Fainelli 		ct->chip.irq_mask = irq_gc_mask_clr_bit;
298a5042de2SFlorian Fainelli 		ct->chip.irq_unmask = irq_gc_mask_set_bit;
299a5042de2SFlorian Fainelli 		ct->chip.irq_ack = irq_gc_noop;
300fd537766SBrian Norris 		gc->suspend = bcm7120_l2_intc_suspend;
301fd537766SBrian Norris 		gc->resume = bcm7120_l2_intc_resume;
302fd537766SBrian Norris 
303fd537766SBrian Norris 		/*
304fd537766SBrian Norris 		 * Initialize mask-cache, in case we need it for
305fd537766SBrian Norris 		 * saving/restoring fwd mask even w/o any child interrupts
306fd537766SBrian Norris 		 * installed
307fd537766SBrian Norris 		 */
308fd537766SBrian Norris 		gc->mask_cache = irq_reg_readl(gc, ct->regs.mask);
309a5042de2SFlorian Fainelli 
310c76acf4dSKevin Cernekee 		if (data->can_wake) {
311c76acf4dSKevin Cernekee 			/* This IRQ chip can wake the system, set all
312c76acf4dSKevin Cernekee 			 * relevant child interupts in wake_enabled mask
313a5042de2SFlorian Fainelli 			 */
314a5042de2SFlorian Fainelli 			gc->wake_enabled = 0xffffffff;
315a5042de2SFlorian Fainelli 			gc->wake_enabled &= ~gc->unused;
316a5042de2SFlorian Fainelli 			ct->chip.irq_set_wake = irq_gc_set_wake;
317a5042de2SFlorian Fainelli 		}
318c76acf4dSKevin Cernekee 	}
319a5042de2SFlorian Fainelli 
320082ce27fSFlorian Fainelli 	pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n",
321082ce27fSFlorian Fainelli 		intc_name, dn, data->num_parent_irqs);
322082ce27fSFlorian Fainelli 
323a5042de2SFlorian Fainelli 	return 0;
324a5042de2SFlorian Fainelli 
325a5042de2SFlorian Fainelli out_free_domain:
326a5042de2SFlorian Fainelli 	irq_domain_remove(data->domain);
3270aef3997SFlorian Fainelli out_free_l1_data:
3280aef3997SFlorian Fainelli 	kfree(data->l1_data);
329a5042de2SFlorian Fainelli out_unmap:
3305b5468cfSKevin Cernekee 	for (idx = 0; idx < MAX_MAPPINGS; idx++) {
3315b5468cfSKevin Cernekee 		if (data->map_base[idx])
3325b5468cfSKevin Cernekee 			iounmap(data->map_base[idx]);
333c76acf4dSKevin Cernekee 	}
334a5042de2SFlorian Fainelli 	kfree(data);
335a5042de2SFlorian Fainelli 	return ret;
336a5042de2SFlorian Fainelli }
337ca40f1b2SKevin Cernekee 
338dde7e6d1SBen Dooks static int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
339ca40f1b2SKevin Cernekee 					     struct device_node *parent)
340ca40f1b2SKevin Cernekee {
341ca40f1b2SKevin Cernekee 	return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
342ca40f1b2SKevin Cernekee 				     "BCM7120 L2");
343ca40f1b2SKevin Cernekee }
344ca40f1b2SKevin Cernekee 
345dde7e6d1SBen Dooks static int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
3467b7230e7SKevin Cernekee 					     struct device_node *parent)
3477b7230e7SKevin Cernekee {
3487b7230e7SKevin Cernekee 	return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
3497b7230e7SKevin Cernekee 				     "BCM3380 L2");
3507b7230e7SKevin Cernekee }
3517b7230e7SKevin Cernekee 
352a4fcbb86SKevin Cernekee IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc",
353ca40f1b2SKevin Cernekee 		bcm7120_l2_intc_probe_7120);
3547b7230e7SKevin Cernekee 
3557b7230e7SKevin Cernekee IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc",
3567b7230e7SKevin Cernekee 		bcm7120_l2_intc_probe_3380);
357