xref: /openbmc/linux/drivers/irqchip/irq-orion.c (revision 70befeda)
19dbd90f1SSebastian Hesselbarth /*
29dbd90f1SSebastian Hesselbarth  * Marvell Orion SoCs IRQ chip driver.
39dbd90f1SSebastian Hesselbarth  *
49dbd90f1SSebastian Hesselbarth  * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
59dbd90f1SSebastian Hesselbarth  *
69dbd90f1SSebastian Hesselbarth  * This file is licensed under the terms of the GNU General Public
79dbd90f1SSebastian Hesselbarth  * License version 2.  This program is licensed "as is" without any
89dbd90f1SSebastian Hesselbarth  * warranty of any kind, whether express or implied.
99dbd90f1SSebastian Hesselbarth  */
109dbd90f1SSebastian Hesselbarth 
119dbd90f1SSebastian Hesselbarth #include <linux/io.h>
129dbd90f1SSebastian Hesselbarth #include <linux/irq.h>
1341a83e06SJoel Porquet #include <linux/irqchip.h>
149dbd90f1SSebastian Hesselbarth #include <linux/of.h>
159dbd90f1SSebastian Hesselbarth #include <linux/of_address.h>
169dbd90f1SSebastian Hesselbarth #include <linux/of_irq.h>
179dbd90f1SSebastian Hesselbarth #include <asm/exception.h>
189dbd90f1SSebastian Hesselbarth #include <asm/mach/irq.h>
199dbd90f1SSebastian Hesselbarth 
209dbd90f1SSebastian Hesselbarth /*
219dbd90f1SSebastian Hesselbarth  * Orion SoC main interrupt controller
229dbd90f1SSebastian Hesselbarth  */
239dbd90f1SSebastian Hesselbarth #define ORION_IRQS_PER_CHIP		32
249dbd90f1SSebastian Hesselbarth 
259dbd90f1SSebastian Hesselbarth #define ORION_IRQ_CAUSE			0x00
269dbd90f1SSebastian Hesselbarth #define ORION_IRQ_MASK			0x04
279dbd90f1SSebastian Hesselbarth #define ORION_IRQ_FIQ_MASK		0x08
289dbd90f1SSebastian Hesselbarth #define ORION_IRQ_ENDP_MASK		0x0c
299dbd90f1SSebastian Hesselbarth 
309dbd90f1SSebastian Hesselbarth static struct irq_domain *orion_irq_domain;
319dbd90f1SSebastian Hesselbarth 
328783dd3aSStephen Boyd static void
orion_handle_irq(struct pt_regs * regs)339dbd90f1SSebastian Hesselbarth __exception_irq_entry orion_handle_irq(struct pt_regs *regs)
349dbd90f1SSebastian Hesselbarth {
359dbd90f1SSebastian Hesselbarth 	struct irq_domain_chip_generic *dgc = orion_irq_domain->gc;
369dbd90f1SSebastian Hesselbarth 	int n, base = 0;
379dbd90f1SSebastian Hesselbarth 
389dbd90f1SSebastian Hesselbarth 	for (n = 0; n < dgc->num_chips; n++, base += ORION_IRQS_PER_CHIP) {
399dbd90f1SSebastian Hesselbarth 		struct irq_chip_generic *gc =
409dbd90f1SSebastian Hesselbarth 			irq_get_domain_generic_chip(orion_irq_domain, base);
419dbd90f1SSebastian Hesselbarth 		u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
429dbd90f1SSebastian Hesselbarth 			gc->mask_cache;
439dbd90f1SSebastian Hesselbarth 		while (stat) {
44bffbc6eaSSebastian Hesselbarth 			u32 hwirq = __fls(stat);
450953fb26SMark Rutland 			generic_handle_domain_irq(orion_irq_domain,
460953fb26SMark Rutland 						  gc->irq_base + hwirq);
479dbd90f1SSebastian Hesselbarth 			stat &= ~(1 << hwirq);
489dbd90f1SSebastian Hesselbarth 		}
499dbd90f1SSebastian Hesselbarth 	}
509dbd90f1SSebastian Hesselbarth }
519dbd90f1SSebastian Hesselbarth 
orion_irq_init(struct device_node * np,struct device_node * parent)529dbd90f1SSebastian Hesselbarth static int __init orion_irq_init(struct device_node *np,
539dbd90f1SSebastian Hesselbarth 				 struct device_node *parent)
549dbd90f1SSebastian Hesselbarth {
559dbd90f1SSebastian Hesselbarth 	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
569dbd90f1SSebastian Hesselbarth 	int n, ret, base, num_chips = 0;
579dbd90f1SSebastian Hesselbarth 	struct resource r;
589dbd90f1SSebastian Hesselbarth 
599dbd90f1SSebastian Hesselbarth 	/* count number of irq chips by valid reg addresses */
60*70befedaSYang Yingliang 	num_chips = of_address_count(np);
619dbd90f1SSebastian Hesselbarth 
629dbd90f1SSebastian Hesselbarth 	orion_irq_domain = irq_domain_add_linear(np,
639dbd90f1SSebastian Hesselbarth 				num_chips * ORION_IRQS_PER_CHIP,
649dbd90f1SSebastian Hesselbarth 				&irq_generic_chip_ops, NULL);
659dbd90f1SSebastian Hesselbarth 	if (!orion_irq_domain)
662ef790dcSRob Herring 		panic("%pOFn: unable to add irq domain\n", np);
679dbd90f1SSebastian Hesselbarth 
689dbd90f1SSebastian Hesselbarth 	ret = irq_alloc_domain_generic_chips(orion_irq_domain,
692ef790dcSRob Herring 				ORION_IRQS_PER_CHIP, 1, np->full_name,
709dbd90f1SSebastian Hesselbarth 				handle_level_irq, clr, 0,
719dbd90f1SSebastian Hesselbarth 				IRQ_GC_INIT_MASK_CACHE);
729dbd90f1SSebastian Hesselbarth 	if (ret)
732ef790dcSRob Herring 		panic("%pOFn: unable to alloc irq domain gc\n", np);
749dbd90f1SSebastian Hesselbarth 
759dbd90f1SSebastian Hesselbarth 	for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) {
769dbd90f1SSebastian Hesselbarth 		struct irq_chip_generic *gc =
779dbd90f1SSebastian Hesselbarth 			irq_get_domain_generic_chip(orion_irq_domain, base);
789dbd90f1SSebastian Hesselbarth 
799dbd90f1SSebastian Hesselbarth 		of_address_to_resource(np, n, &r);
809dbd90f1SSebastian Hesselbarth 
819dbd90f1SSebastian Hesselbarth 		if (!request_mem_region(r.start, resource_size(&r), np->name))
822ef790dcSRob Herring 			panic("%pOFn: unable to request mem region %d",
832ef790dcSRob Herring 			      np, n);
849dbd90f1SSebastian Hesselbarth 
859dbd90f1SSebastian Hesselbarth 		gc->reg_base = ioremap(r.start, resource_size(&r));
869dbd90f1SSebastian Hesselbarth 		if (!gc->reg_base)
872ef790dcSRob Herring 			panic("%pOFn: unable to map resource %d", np, n);
889dbd90f1SSebastian Hesselbarth 
899dbd90f1SSebastian Hesselbarth 		gc->chip_types[0].regs.mask = ORION_IRQ_MASK;
909dbd90f1SSebastian Hesselbarth 		gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
919dbd90f1SSebastian Hesselbarth 		gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
929dbd90f1SSebastian Hesselbarth 
939dbd90f1SSebastian Hesselbarth 		/* mask all interrupts */
949dbd90f1SSebastian Hesselbarth 		writel(0, gc->reg_base + ORION_IRQ_MASK);
959dbd90f1SSebastian Hesselbarth 	}
969dbd90f1SSebastian Hesselbarth 
979dbd90f1SSebastian Hesselbarth 	set_handle_irq(orion_handle_irq);
989dbd90f1SSebastian Hesselbarth 	return 0;
999dbd90f1SSebastian Hesselbarth }
1009dbd90f1SSebastian Hesselbarth IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
1019dbd90f1SSebastian Hesselbarth 
1029dbd90f1SSebastian Hesselbarth /*
1039dbd90f1SSebastian Hesselbarth  * Orion SoC bridge interrupt controller
1049dbd90f1SSebastian Hesselbarth  */
1059dbd90f1SSebastian Hesselbarth #define ORION_BRIDGE_IRQ_CAUSE	0x00
1069dbd90f1SSebastian Hesselbarth #define ORION_BRIDGE_IRQ_MASK	0x04
1079dbd90f1SSebastian Hesselbarth 
orion_bridge_irq_handler(struct irq_desc * desc)108bd0b9ac4SThomas Gleixner static void orion_bridge_irq_handler(struct irq_desc *desc)
1099dbd90f1SSebastian Hesselbarth {
1105b29264cSJiang Liu 	struct irq_domain *d = irq_desc_get_handler_data(desc);
111d86e9af6SAndrew Lunn 
112d86e9af6SAndrew Lunn 	struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
1139dbd90f1SSebastian Hesselbarth 	u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) &
1149dbd90f1SSebastian Hesselbarth 		   gc->mask_cache;
1159dbd90f1SSebastian Hesselbarth 
1169dbd90f1SSebastian Hesselbarth 	while (stat) {
117bffbc6eaSSebastian Hesselbarth 		u32 hwirq = __fls(stat);
1189dbd90f1SSebastian Hesselbarth 
119046a6ee2SMarc Zyngier 		generic_handle_domain_irq(d, gc->irq_base + hwirq);
1209dbd90f1SSebastian Hesselbarth 		stat &= ~(1 << hwirq);
1219dbd90f1SSebastian Hesselbarth 	}
1229dbd90f1SSebastian Hesselbarth }
1239dbd90f1SSebastian Hesselbarth 
124e0318ec3SSebastian Hesselbarth /*
125e0318ec3SSebastian Hesselbarth  * Bridge IRQ_CAUSE is asserted regardless of IRQ_MASK register.
126e0318ec3SSebastian Hesselbarth  * To avoid interrupt events on stale irqs, we clear them before unmask.
127e0318ec3SSebastian Hesselbarth  */
orion_bridge_irq_startup(struct irq_data * d)128e0318ec3SSebastian Hesselbarth static unsigned int orion_bridge_irq_startup(struct irq_data *d)
129e0318ec3SSebastian Hesselbarth {
130e0318ec3SSebastian Hesselbarth 	struct irq_chip_type *ct = irq_data_get_chip_type(d);
131e0318ec3SSebastian Hesselbarth 
132e0318ec3SSebastian Hesselbarth 	ct->chip.irq_ack(d);
133e0318ec3SSebastian Hesselbarth 	ct->chip.irq_unmask(d);
134e0318ec3SSebastian Hesselbarth 	return 0;
135e0318ec3SSebastian Hesselbarth }
136e0318ec3SSebastian Hesselbarth 
orion_bridge_irq_init(struct device_node * np,struct device_node * parent)1379dbd90f1SSebastian Hesselbarth static int __init orion_bridge_irq_init(struct device_node *np,
1389dbd90f1SSebastian Hesselbarth 					struct device_node *parent)
1399dbd90f1SSebastian Hesselbarth {
1409dbd90f1SSebastian Hesselbarth 	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
1419dbd90f1SSebastian Hesselbarth 	struct resource r;
1429dbd90f1SSebastian Hesselbarth 	struct irq_domain *domain;
1439dbd90f1SSebastian Hesselbarth 	struct irq_chip_generic *gc;
1449dbd90f1SSebastian Hesselbarth 	int ret, irq, nrirqs = 32;
1459dbd90f1SSebastian Hesselbarth 
1469dbd90f1SSebastian Hesselbarth 	/* get optional number of interrupts provided */
1479dbd90f1SSebastian Hesselbarth 	of_property_read_u32(np, "marvell,#interrupts", &nrirqs);
1489dbd90f1SSebastian Hesselbarth 
1499dbd90f1SSebastian Hesselbarth 	domain = irq_domain_add_linear(np, nrirqs,
1509dbd90f1SSebastian Hesselbarth 				       &irq_generic_chip_ops, NULL);
1519dbd90f1SSebastian Hesselbarth 	if (!domain) {
1522ef790dcSRob Herring 		pr_err("%pOFn: unable to add irq domain\n", np);
1539dbd90f1SSebastian Hesselbarth 		return -ENOMEM;
1549dbd90f1SSebastian Hesselbarth 	}
1559dbd90f1SSebastian Hesselbarth 
1569dbd90f1SSebastian Hesselbarth 	ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
1575f40067fSSebastian Hesselbarth 			     handle_edge_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
1589dbd90f1SSebastian Hesselbarth 	if (ret) {
1592ef790dcSRob Herring 		pr_err("%pOFn: unable to alloc irq domain gc\n", np);
1609dbd90f1SSebastian Hesselbarth 		return ret;
1619dbd90f1SSebastian Hesselbarth 	}
1629dbd90f1SSebastian Hesselbarth 
1639dbd90f1SSebastian Hesselbarth 	ret = of_address_to_resource(np, 0, &r);
1649dbd90f1SSebastian Hesselbarth 	if (ret) {
1652ef790dcSRob Herring 		pr_err("%pOFn: unable to get resource\n", np);
1669dbd90f1SSebastian Hesselbarth 		return ret;
1679dbd90f1SSebastian Hesselbarth 	}
1689dbd90f1SSebastian Hesselbarth 
1699dbd90f1SSebastian Hesselbarth 	if (!request_mem_region(r.start, resource_size(&r), np->name)) {
1709dbd90f1SSebastian Hesselbarth 		pr_err("%s: unable to request mem region\n", np->name);
1719dbd90f1SSebastian Hesselbarth 		return -ENOMEM;
1729dbd90f1SSebastian Hesselbarth 	}
1739dbd90f1SSebastian Hesselbarth 
1749dbd90f1SSebastian Hesselbarth 	/* Map the parent interrupt for the chained handler */
1759dbd90f1SSebastian Hesselbarth 	irq = irq_of_parse_and_map(np, 0);
1769dbd90f1SSebastian Hesselbarth 	if (irq <= 0) {
1772ef790dcSRob Herring 		pr_err("%pOFn: unable to parse irq\n", np);
1789dbd90f1SSebastian Hesselbarth 		return -EINVAL;
1799dbd90f1SSebastian Hesselbarth 	}
1809dbd90f1SSebastian Hesselbarth 
1819dbd90f1SSebastian Hesselbarth 	gc = irq_get_domain_generic_chip(domain, 0);
1829dbd90f1SSebastian Hesselbarth 	gc->reg_base = ioremap(r.start, resource_size(&r));
1839dbd90f1SSebastian Hesselbarth 	if (!gc->reg_base) {
1842ef790dcSRob Herring 		pr_err("%pOFn: unable to map resource\n", np);
1859dbd90f1SSebastian Hesselbarth 		return -ENOMEM;
1869dbd90f1SSebastian Hesselbarth 	}
1879dbd90f1SSebastian Hesselbarth 
1889dbd90f1SSebastian Hesselbarth 	gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE;
1899dbd90f1SSebastian Hesselbarth 	gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK;
190e0318ec3SSebastian Hesselbarth 	gc->chip_types[0].chip.irq_startup = orion_bridge_irq_startup;
1919dbd90f1SSebastian Hesselbarth 	gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
1929dbd90f1SSebastian Hesselbarth 	gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
1939dbd90f1SSebastian Hesselbarth 	gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
1949dbd90f1SSebastian Hesselbarth 
1957b119fd1SSebastian Hesselbarth 	/* mask and clear all interrupts */
1969dbd90f1SSebastian Hesselbarth 	writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK);
1977b119fd1SSebastian Hesselbarth 	writel(0, gc->reg_base + ORION_BRIDGE_IRQ_CAUSE);
1989dbd90f1SSebastian Hesselbarth 
19907d22c23SThomas Gleixner 	irq_set_chained_handler_and_data(irq, orion_bridge_irq_handler,
20007d22c23SThomas Gleixner 					 domain);
2019dbd90f1SSebastian Hesselbarth 
2029dbd90f1SSebastian Hesselbarth 	return 0;
2039dbd90f1SSebastian Hesselbarth }
2049dbd90f1SSebastian Hesselbarth IRQCHIP_DECLARE(orion_bridge_intc,
2059dbd90f1SSebastian Hesselbarth 		"marvell,orion-bridge-intc", orion_bridge_irq_init);
206