xref: /linux/drivers/irqchip/irq-mscc-ocelot.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
119d99164SAlexandre Belloni // SPDX-License-Identifier: (GPL-2.0 OR MIT)
219d99164SAlexandre Belloni /*
319d99164SAlexandre Belloni  * Microsemi Ocelot IRQ controller driver
419d99164SAlexandre Belloni  *
519d99164SAlexandre Belloni  * Copyright (c) 2017 Microsemi Corporation
619d99164SAlexandre Belloni  */
719d99164SAlexandre Belloni #include <linux/bitops.h>
819d99164SAlexandre Belloni #include <linux/irq.h>
919d99164SAlexandre Belloni #include <linux/of_address.h>
1019d99164SAlexandre Belloni #include <linux/of_irq.h>
1119d99164SAlexandre Belloni #include <linux/irqchip.h>
1219d99164SAlexandre Belloni #include <linux/irqchip/chained_irq.h>
1319d99164SAlexandre Belloni #include <linux/interrupt.h>
1419d99164SAlexandre Belloni 
155f0c75e7SGregory CLEMENT #define ICPU_CFG_INTR_DST_INTR_IDENT(_p, x) ((_p)->reg_off_ident + 0x4 * (x))
165f0c75e7SGregory CLEMENT #define ICPU_CFG_INTR_INTR_TRIGGER(_p, x)   ((_p)->reg_off_trigger + 0x4 * (x))
1719d99164SAlexandre Belloni 
185f0c75e7SGregory CLEMENT #define FLAGS_HAS_TRIGGER	BIT(0)
19ffce73d4SGregory CLEMENT #define FLAGS_NEED_INIT_ENABLE	BIT(1)
205f0c75e7SGregory CLEMENT 
215f0c75e7SGregory CLEMENT struct chip_props {
225f0c75e7SGregory CLEMENT 	u8 flags;
235f0c75e7SGregory CLEMENT 	u8 reg_off_sticky;
245f0c75e7SGregory CLEMENT 	u8 reg_off_ena;
255f0c75e7SGregory CLEMENT 	u8 reg_off_ena_clr;
265f0c75e7SGregory CLEMENT 	u8 reg_off_ena_set;
275f0c75e7SGregory CLEMENT 	u8 reg_off_ident;
285f0c75e7SGregory CLEMENT 	u8 reg_off_trigger;
295f0c75e7SGregory CLEMENT 	u8 reg_off_ena_irq0;
305f0c75e7SGregory CLEMENT 	u8 n_irq;
315f0c75e7SGregory CLEMENT };
325f0c75e7SGregory CLEMENT 
335f0c75e7SGregory CLEMENT static struct chip_props ocelot_props = {
345f0c75e7SGregory CLEMENT 	.flags			= FLAGS_HAS_TRIGGER,
355f0c75e7SGregory CLEMENT 	.reg_off_sticky		= 0x10,
365f0c75e7SGregory CLEMENT 	.reg_off_ena		= 0x18,
375f0c75e7SGregory CLEMENT 	.reg_off_ena_clr	= 0x1c,
385f0c75e7SGregory CLEMENT 	.reg_off_ena_set	= 0x20,
395f0c75e7SGregory CLEMENT 	.reg_off_ident		= 0x38,
405f0c75e7SGregory CLEMENT 	.reg_off_trigger	= 0x5c,
415f0c75e7SGregory CLEMENT 	.n_irq			= 24,
425f0c75e7SGregory CLEMENT };
4319d99164SAlexandre Belloni 
447efdfbd1SGregory CLEMENT static struct chip_props serval_props = {
457efdfbd1SGregory CLEMENT 	.flags			= FLAGS_HAS_TRIGGER,
467efdfbd1SGregory CLEMENT 	.reg_off_sticky		= 0xc,
477efdfbd1SGregory CLEMENT 	.reg_off_ena		= 0x14,
487efdfbd1SGregory CLEMENT 	.reg_off_ena_clr	= 0x18,
497efdfbd1SGregory CLEMENT 	.reg_off_ena_set	= 0x1c,
507efdfbd1SGregory CLEMENT 	.reg_off_ident		= 0x20,
517efdfbd1SGregory CLEMENT 	.reg_off_trigger	= 0x4,
527efdfbd1SGregory CLEMENT 	.n_irq			= 24,
537efdfbd1SGregory CLEMENT };
547efdfbd1SGregory CLEMENT 
55ffce73d4SGregory CLEMENT static struct chip_props luton_props = {
56ffce73d4SGregory CLEMENT 	.flags			= FLAGS_NEED_INIT_ENABLE,
57ffce73d4SGregory CLEMENT 	.reg_off_sticky		= 0,
58ffce73d4SGregory CLEMENT 	.reg_off_ena		= 0x4,
59ffce73d4SGregory CLEMENT 	.reg_off_ena_clr	= 0x8,
60ffce73d4SGregory CLEMENT 	.reg_off_ena_set	= 0xc,
61ffce73d4SGregory CLEMENT 	.reg_off_ident		= 0x18,
62ffce73d4SGregory CLEMENT 	.reg_off_ena_irq0	= 0x14,
63ffce73d4SGregory CLEMENT 	.n_irq			= 28,
64ffce73d4SGregory CLEMENT };
65ffce73d4SGregory CLEMENT 
66550c1424SGregory CLEMENT static struct chip_props jaguar2_props = {
67550c1424SGregory CLEMENT 	.flags			= FLAGS_HAS_TRIGGER,
68550c1424SGregory CLEMENT 	.reg_off_sticky		= 0x10,
69550c1424SGregory CLEMENT 	.reg_off_ena		= 0x18,
70550c1424SGregory CLEMENT 	.reg_off_ena_clr	= 0x1c,
71550c1424SGregory CLEMENT 	.reg_off_ena_set	= 0x20,
72550c1424SGregory CLEMENT 	.reg_off_ident		= 0x38,
73550c1424SGregory CLEMENT 	.reg_off_trigger	= 0x5c,
74550c1424SGregory CLEMENT 	.n_irq			= 29,
75550c1424SGregory CLEMENT };
76550c1424SGregory CLEMENT 
7719d99164SAlexandre Belloni static void ocelot_irq_unmask(struct irq_data *data)
7819d99164SAlexandre Belloni {
7919d99164SAlexandre Belloni 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
805f0c75e7SGregory CLEMENT 	struct irq_domain *d = data->domain;
815f0c75e7SGregory CLEMENT 	struct chip_props *p = d->host_data;
8219d99164SAlexandre Belloni 	struct irq_chip_type *ct = irq_data_get_chip_type(data);
8319d99164SAlexandre Belloni 	unsigned int mask = data->mask;
8419d99164SAlexandre Belloni 	u32 val;
8519d99164SAlexandre Belloni 
8619d99164SAlexandre Belloni 	irq_gc_lock(gc);
875f0c75e7SGregory CLEMENT 	val = irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(p, 0)) |
885f0c75e7SGregory CLEMENT 		irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(p, 1));
8919d99164SAlexandre Belloni 	if (!(val & mask))
905f0c75e7SGregory CLEMENT 		irq_reg_writel(gc, mask, p->reg_off_sticky);
9119d99164SAlexandre Belloni 
9219d99164SAlexandre Belloni 	*ct->mask_cache &= ~mask;
935f0c75e7SGregory CLEMENT 	irq_reg_writel(gc, mask, p->reg_off_ena_set);
9419d99164SAlexandre Belloni 	irq_gc_unlock(gc);
9519d99164SAlexandre Belloni }
9619d99164SAlexandre Belloni 
9719d99164SAlexandre Belloni static void ocelot_irq_handler(struct irq_desc *desc)
9819d99164SAlexandre Belloni {
9919d99164SAlexandre Belloni 	struct irq_chip *chip = irq_desc_get_chip(desc);
10019d99164SAlexandre Belloni 	struct irq_domain *d = irq_desc_get_handler_data(desc);
1015f0c75e7SGregory CLEMENT 	struct chip_props *p = d->host_data;
10219d99164SAlexandre Belloni 	struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
1035f0c75e7SGregory CLEMENT 	u32 reg = irq_reg_readl(gc, ICPU_CFG_INTR_DST_INTR_IDENT(p, 0));
10419d99164SAlexandre Belloni 
10519d99164SAlexandre Belloni 	chained_irq_enter(chip, desc);
10619d99164SAlexandre Belloni 
10719d99164SAlexandre Belloni 	while (reg) {
10819d99164SAlexandre Belloni 		u32 hwirq = __fls(reg);
10919d99164SAlexandre Belloni 
110*046a6ee2SMarc Zyngier 		generic_handle_domain_irq(d, hwirq);
11119d99164SAlexandre Belloni 		reg &= ~(BIT(hwirq));
11219d99164SAlexandre Belloni 	}
11319d99164SAlexandre Belloni 
11419d99164SAlexandre Belloni 	chained_irq_exit(chip, desc);
11519d99164SAlexandre Belloni }
11619d99164SAlexandre Belloni 
1175f0c75e7SGregory CLEMENT static int __init vcoreiii_irq_init(struct device_node *node,
1185f0c75e7SGregory CLEMENT 				    struct device_node *parent,
1195f0c75e7SGregory CLEMENT 				    struct chip_props *p)
12019d99164SAlexandre Belloni {
12119d99164SAlexandre Belloni 	struct irq_domain *domain;
12219d99164SAlexandre Belloni 	struct irq_chip_generic *gc;
12319d99164SAlexandre Belloni 	int parent_irq, ret;
12419d99164SAlexandre Belloni 
12519d99164SAlexandre Belloni 	parent_irq = irq_of_parse_and_map(node, 0);
12619d99164SAlexandre Belloni 	if (!parent_irq)
12719d99164SAlexandre Belloni 		return -EINVAL;
12819d99164SAlexandre Belloni 
1295f0c75e7SGregory CLEMENT 	domain = irq_domain_add_linear(node, p->n_irq,
13019d99164SAlexandre Belloni 				       &irq_generic_chip_ops, NULL);
13119d99164SAlexandre Belloni 	if (!domain) {
132f9c75bcaSYangtao Li 		pr_err("%pOFn: unable to add irq domain\n", node);
13319d99164SAlexandre Belloni 		return -ENOMEM;
13419d99164SAlexandre Belloni 	}
13519d99164SAlexandre Belloni 
1365f0c75e7SGregory CLEMENT 	ret = irq_alloc_domain_generic_chips(domain, p->n_irq, 1,
13719d99164SAlexandre Belloni 					     "icpu", handle_level_irq,
13819d99164SAlexandre Belloni 					     0, 0, 0);
13919d99164SAlexandre Belloni 	if (ret) {
140f9c75bcaSYangtao Li 		pr_err("%pOFn: unable to alloc irq domain gc\n", node);
14119d99164SAlexandre Belloni 		goto err_domain_remove;
14219d99164SAlexandre Belloni 	}
14319d99164SAlexandre Belloni 
14419d99164SAlexandre Belloni 	gc = irq_get_domain_generic_chip(domain, 0);
14519d99164SAlexandre Belloni 	gc->reg_base = of_iomap(node, 0);
14619d99164SAlexandre Belloni 	if (!gc->reg_base) {
147f9c75bcaSYangtao Li 		pr_err("%pOFn: unable to map resource\n", node);
14819d99164SAlexandre Belloni 		ret = -ENOMEM;
14919d99164SAlexandre Belloni 		goto err_gc_free;
15019d99164SAlexandre Belloni 	}
15119d99164SAlexandre Belloni 
15219d99164SAlexandre Belloni 	gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
153ffce73d4SGregory CLEMENT 	gc->chip_types[0].regs.ack = p->reg_off_sticky;
154ffce73d4SGregory CLEMENT 	if (p->flags & FLAGS_HAS_TRIGGER) {
155ffce73d4SGregory CLEMENT 		gc->chip_types[0].regs.mask = p->reg_off_ena_clr;
15619d99164SAlexandre Belloni 		gc->chip_types[0].chip.irq_unmask = ocelot_irq_unmask;
157ffce73d4SGregory CLEMENT 		gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
158ffce73d4SGregory CLEMENT 	} else {
159ffce73d4SGregory CLEMENT 		gc->chip_types[0].regs.enable = p->reg_off_ena_set;
160ffce73d4SGregory CLEMENT 		gc->chip_types[0].regs.disable = p->reg_off_ena_clr;
161ffce73d4SGregory CLEMENT 		gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
162ffce73d4SGregory CLEMENT 		gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
163ffce73d4SGregory CLEMENT 	}
16419d99164SAlexandre Belloni 
16519d99164SAlexandre Belloni 	/* Mask and ack all interrupts */
1665f0c75e7SGregory CLEMENT 	irq_reg_writel(gc, 0, p->reg_off_ena);
1675f0c75e7SGregory CLEMENT 	irq_reg_writel(gc, 0xffffffff, p->reg_off_sticky);
16819d99164SAlexandre Belloni 
169ffce73d4SGregory CLEMENT 	/* Overall init */
170ffce73d4SGregory CLEMENT 	if (p->flags & FLAGS_NEED_INIT_ENABLE)
171ffce73d4SGregory CLEMENT 		irq_reg_writel(gc, BIT(0), p->reg_off_ena_irq0);
172ffce73d4SGregory CLEMENT 
1735f0c75e7SGregory CLEMENT 	domain->host_data = p;
17419d99164SAlexandre Belloni 	irq_set_chained_handler_and_data(parent_irq, ocelot_irq_handler,
17519d99164SAlexandre Belloni 					 domain);
17619d99164SAlexandre Belloni 
17719d99164SAlexandre Belloni 	return 0;
17819d99164SAlexandre Belloni 
17919d99164SAlexandre Belloni err_gc_free:
18019d99164SAlexandre Belloni 	irq_free_generic_chip(gc);
18119d99164SAlexandre Belloni 
18219d99164SAlexandre Belloni err_domain_remove:
18319d99164SAlexandre Belloni 	irq_domain_remove(domain);
18419d99164SAlexandre Belloni 
18519d99164SAlexandre Belloni 	return ret;
18619d99164SAlexandre Belloni }
1875f0c75e7SGregory CLEMENT 
1885f0c75e7SGregory CLEMENT static int __init ocelot_irq_init(struct device_node *node,
1895f0c75e7SGregory CLEMENT 				  struct device_node *parent)
1905f0c75e7SGregory CLEMENT {
1915f0c75e7SGregory CLEMENT 	return vcoreiii_irq_init(node, parent, &ocelot_props);
1925f0c75e7SGregory CLEMENT }
1935f0c75e7SGregory CLEMENT 
19419d99164SAlexandre Belloni IRQCHIP_DECLARE(ocelot_icpu, "mscc,ocelot-icpu-intr", ocelot_irq_init);
195ffce73d4SGregory CLEMENT 
1967efdfbd1SGregory CLEMENT static int __init serval_irq_init(struct device_node *node,
1977efdfbd1SGregory CLEMENT 				  struct device_node *parent)
1987efdfbd1SGregory CLEMENT {
1997efdfbd1SGregory CLEMENT 	return vcoreiii_irq_init(node, parent, &serval_props);
2007efdfbd1SGregory CLEMENT }
2017efdfbd1SGregory CLEMENT 
2027efdfbd1SGregory CLEMENT IRQCHIP_DECLARE(serval_icpu, "mscc,serval-icpu-intr", serval_irq_init);
2037efdfbd1SGregory CLEMENT 
204ffce73d4SGregory CLEMENT static int __init luton_irq_init(struct device_node *node,
205ffce73d4SGregory CLEMENT 				 struct device_node *parent)
206ffce73d4SGregory CLEMENT {
207ffce73d4SGregory CLEMENT 	return vcoreiii_irq_init(node, parent, &luton_props);
208ffce73d4SGregory CLEMENT }
209ffce73d4SGregory CLEMENT 
210ffce73d4SGregory CLEMENT IRQCHIP_DECLARE(luton_icpu, "mscc,luton-icpu-intr", luton_irq_init);
211550c1424SGregory CLEMENT 
212550c1424SGregory CLEMENT static int __init jaguar2_irq_init(struct device_node *node,
213550c1424SGregory CLEMENT 				   struct device_node *parent)
214550c1424SGregory CLEMENT {
215550c1424SGregory CLEMENT 	return vcoreiii_irq_init(node, parent, &jaguar2_props);
216550c1424SGregory CLEMENT }
217550c1424SGregory CLEMENT 
218550c1424SGregory CLEMENT IRQCHIP_DECLARE(jaguar2_icpu, "mscc,jaguar2-icpu-intr", jaguar2_irq_init);
219