xref: /openbmc/linux/arch/powerpc/platforms/embedded6xx/hlwd-pic.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29c21025cSAlbert Herranz /*
39c21025cSAlbert Herranz  * arch/powerpc/platforms/embedded6xx/hlwd-pic.c
49c21025cSAlbert Herranz  *
59c21025cSAlbert Herranz  * Nintendo Wii "Hollywood" interrupt controller support.
69c21025cSAlbert Herranz  * Copyright (C) 2009 The GameCube Linux Team
79c21025cSAlbert Herranz  * Copyright (C) 2009 Albert Herranz
89c21025cSAlbert Herranz  */
99c21025cSAlbert Herranz #define DRV_MODULE_NAME "hlwd-pic"
109c21025cSAlbert Herranz #define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
119c21025cSAlbert Herranz 
129c21025cSAlbert Herranz #include <linux/kernel.h>
139c21025cSAlbert Herranz #include <linux/irq.h>
149c21025cSAlbert Herranz #include <linux/of.h>
1526a2056eSRob Herring #include <linux/of_address.h>
1626a2056eSRob Herring #include <linux/of_irq.h>
179c21025cSAlbert Herranz #include <asm/io.h>
189c21025cSAlbert Herranz 
199c21025cSAlbert Herranz #include "hlwd-pic.h"
209c21025cSAlbert Herranz 
219c21025cSAlbert Herranz #define HLWD_NR_IRQS	32
229c21025cSAlbert Herranz 
239c21025cSAlbert Herranz /*
249c21025cSAlbert Herranz  * Each interrupt has a corresponding bit in both
259c21025cSAlbert Herranz  * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.
269c21025cSAlbert Herranz  *
279c21025cSAlbert Herranz  * Enabling/disabling an interrupt line involves asserting/clearing
289c21025cSAlbert Herranz  * the corresponding bit in IMR. ACK'ing a request simply involves
299c21025cSAlbert Herranz  * asserting the corresponding bit in ICR.
309c21025cSAlbert Herranz  */
319c21025cSAlbert Herranz #define HW_BROADWAY_ICR		0x00
329c21025cSAlbert Herranz #define HW_BROADWAY_IMR		0x04
339dcb3df4SJonathan Neuschäfer #define HW_STARLET_ICR		0x08
349dcb3df4SJonathan Neuschäfer #define HW_STARLET_IMR		0x0c
359c21025cSAlbert Herranz 
369c21025cSAlbert Herranz 
379c21025cSAlbert Herranz /*
389c21025cSAlbert Herranz  * IRQ chip hooks.
399c21025cSAlbert Herranz  *
409c21025cSAlbert Herranz  */
419c21025cSAlbert Herranz 
hlwd_pic_mask_and_ack(struct irq_data * d)420bf8878eSLennert Buytenhek static void hlwd_pic_mask_and_ack(struct irq_data *d)
439c21025cSAlbert Herranz {
44476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
450bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
469c21025cSAlbert Herranz 	u32 mask = 1 << irq;
479c21025cSAlbert Herranz 
489c21025cSAlbert Herranz 	clrbits32(io_base + HW_BROADWAY_IMR, mask);
499c21025cSAlbert Herranz 	out_be32(io_base + HW_BROADWAY_ICR, mask);
509c21025cSAlbert Herranz }
519c21025cSAlbert Herranz 
hlwd_pic_ack(struct irq_data * d)520bf8878eSLennert Buytenhek static void hlwd_pic_ack(struct irq_data *d)
539c21025cSAlbert Herranz {
54476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
550bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
569c21025cSAlbert Herranz 
579c21025cSAlbert Herranz 	out_be32(io_base + HW_BROADWAY_ICR, 1 << irq);
589c21025cSAlbert Herranz }
599c21025cSAlbert Herranz 
hlwd_pic_mask(struct irq_data * d)600bf8878eSLennert Buytenhek static void hlwd_pic_mask(struct irq_data *d)
619c21025cSAlbert Herranz {
62476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
630bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
649c21025cSAlbert Herranz 
659c21025cSAlbert Herranz 	clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
669c21025cSAlbert Herranz }
679c21025cSAlbert Herranz 
hlwd_pic_unmask(struct irq_data * d)680bf8878eSLennert Buytenhek static void hlwd_pic_unmask(struct irq_data *d)
699c21025cSAlbert Herranz {
70476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
710bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
729c21025cSAlbert Herranz 
739c21025cSAlbert Herranz 	setbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
749dcb3df4SJonathan Neuschäfer 
759dcb3df4SJonathan Neuschäfer 	/* Make sure the ARM (aka. Starlet) doesn't handle this interrupt. */
769dcb3df4SJonathan Neuschäfer 	clrbits32(io_base + HW_STARLET_IMR, 1 << irq);
779c21025cSAlbert Herranz }
789c21025cSAlbert Herranz 
799c21025cSAlbert Herranz 
809c21025cSAlbert Herranz static struct irq_chip hlwd_pic = {
819c21025cSAlbert Herranz 	.name		= "hlwd-pic",
820bf8878eSLennert Buytenhek 	.irq_ack	= hlwd_pic_ack,
830bf8878eSLennert Buytenhek 	.irq_mask_ack	= hlwd_pic_mask_and_ack,
840bf8878eSLennert Buytenhek 	.irq_mask	= hlwd_pic_mask,
850bf8878eSLennert Buytenhek 	.irq_unmask	= hlwd_pic_unmask,
869c21025cSAlbert Herranz };
879c21025cSAlbert Herranz 
889c21025cSAlbert Herranz /*
899c21025cSAlbert Herranz  * IRQ host hooks.
909c21025cSAlbert Herranz  *
919c21025cSAlbert Herranz  */
929c21025cSAlbert Herranz 
93bae1d8f1SGrant Likely static struct irq_domain *hlwd_irq_host;
949c21025cSAlbert Herranz 
hlwd_pic_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hwirq)95bae1d8f1SGrant Likely static int hlwd_pic_map(struct irq_domain *h, unsigned int virq,
969c21025cSAlbert Herranz 			   irq_hw_number_t hwirq)
979c21025cSAlbert Herranz {
98ec775d0eSThomas Gleixner 	irq_set_chip_data(virq, h->host_data);
9998488db9SThomas Gleixner 	irq_set_status_flags(virq, IRQ_LEVEL);
100ec775d0eSThomas Gleixner 	irq_set_chip_and_handler(virq, &hlwd_pic, handle_level_irq);
1019c21025cSAlbert Herranz 	return 0;
1029c21025cSAlbert Herranz }
1039c21025cSAlbert Herranz 
1049f70b8ebSGrant Likely static const struct irq_domain_ops hlwd_irq_domain_ops = {
1059c21025cSAlbert Herranz 	.map = hlwd_pic_map,
1069c21025cSAlbert Herranz };
1079c21025cSAlbert Herranz 
__hlwd_pic_get_irq(struct irq_domain * h)108bae1d8f1SGrant Likely static unsigned int __hlwd_pic_get_irq(struct irq_domain *h)
1099c21025cSAlbert Herranz {
1109c21025cSAlbert Herranz 	void __iomem *io_base = h->host_data;
1119c21025cSAlbert Herranz 	u32 irq_status;
1129c21025cSAlbert Herranz 
1139c21025cSAlbert Herranz 	irq_status = in_be32(io_base + HW_BROADWAY_ICR) &
1149c21025cSAlbert Herranz 		     in_be32(io_base + HW_BROADWAY_IMR);
1159c21025cSAlbert Herranz 	if (irq_status == 0)
116ef24ba70SMichael Ellerman 		return 0;	/* no more IRQs pending */
1179c21025cSAlbert Herranz 
1182c899658SMarc Zyngier 	return __ffs(irq_status);
1199c21025cSAlbert Herranz }
1209c21025cSAlbert Herranz 
hlwd_pic_irq_cascade(struct irq_desc * desc)121bd0b9ac4SThomas Gleixner static void hlwd_pic_irq_cascade(struct irq_desc *desc)
1229c21025cSAlbert Herranz {
123ec775d0eSThomas Gleixner 	struct irq_chip *chip = irq_desc_get_chip(desc);
124c1231a78SJiang Liu 	struct irq_domain *irq_domain = irq_desc_get_handler_data(desc);
1252c899658SMarc Zyngier 	unsigned int hwirq;
1269c21025cSAlbert Herranz 
1277ccec3e7SAlbert Herranz 	raw_spin_lock(&desc->lock);
1280bf8878eSLennert Buytenhek 	chip->irq_mask(&desc->irq_data); /* IRQ_LEVEL */
1297ccec3e7SAlbert Herranz 	raw_spin_unlock(&desc->lock);
1309c21025cSAlbert Herranz 
1312c899658SMarc Zyngier 	hwirq = __hlwd_pic_get_irq(irq_domain);
1322c899658SMarc Zyngier 	if (hwirq)
1332c899658SMarc Zyngier 		generic_handle_domain_irq(irq_domain, hwirq);
1349c21025cSAlbert Herranz 	else
1359c21025cSAlbert Herranz 		pr_err("spurious interrupt!\n");
1369c21025cSAlbert Herranz 
1377ccec3e7SAlbert Herranz 	raw_spin_lock(&desc->lock);
1380bf8878eSLennert Buytenhek 	chip->irq_ack(&desc->irq_data); /* IRQ_LEVEL */
13998488db9SThomas Gleixner 	if (!irqd_irq_disabled(&desc->irq_data) && chip->irq_unmask)
1400bf8878eSLennert Buytenhek 		chip->irq_unmask(&desc->irq_data);
1417ccec3e7SAlbert Herranz 	raw_spin_unlock(&desc->lock);
1429c21025cSAlbert Herranz }
1439c21025cSAlbert Herranz 
1449c21025cSAlbert Herranz /*
1459c21025cSAlbert Herranz  * Platform hooks.
1469c21025cSAlbert Herranz  *
1479c21025cSAlbert Herranz  */
1489c21025cSAlbert Herranz 
__hlwd_quiesce(void __iomem * io_base)1499c21025cSAlbert Herranz static void __hlwd_quiesce(void __iomem *io_base)
1509c21025cSAlbert Herranz {
1519c21025cSAlbert Herranz 	/* mask and ack all IRQs */
1529c21025cSAlbert Herranz 	out_be32(io_base + HW_BROADWAY_IMR, 0);
1539c21025cSAlbert Herranz 	out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff);
1549c21025cSAlbert Herranz }
1559c21025cSAlbert Herranz 
hlwd_pic_init(struct device_node * np)156c0dc225aSNick Child static struct irq_domain *__init hlwd_pic_init(struct device_node *np)
1579c21025cSAlbert Herranz {
158bae1d8f1SGrant Likely 	struct irq_domain *irq_domain;
1599c21025cSAlbert Herranz 	struct resource res;
1609c21025cSAlbert Herranz 	void __iomem *io_base;
1619c21025cSAlbert Herranz 	int retval;
1629c21025cSAlbert Herranz 
1639c21025cSAlbert Herranz 	retval = of_address_to_resource(np, 0, &res);
1649c21025cSAlbert Herranz 	if (retval) {
1659c21025cSAlbert Herranz 		pr_err("no io memory range found\n");
1669c21025cSAlbert Herranz 		return NULL;
1679c21025cSAlbert Herranz 	}
1689c21025cSAlbert Herranz 	io_base = ioremap(res.start, resource_size(&res));
1699c21025cSAlbert Herranz 	if (!io_base) {
1709c21025cSAlbert Herranz 		pr_err("ioremap failed\n");
1719c21025cSAlbert Herranz 		return NULL;
1729c21025cSAlbert Herranz 	}
1739c21025cSAlbert Herranz 
174*7b69600dSRandy Dunlap 	pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);
1759c21025cSAlbert Herranz 
1769c21025cSAlbert Herranz 	__hlwd_quiesce(io_base);
1779c21025cSAlbert Herranz 
178a8db8cf0SGrant Likely 	irq_domain = irq_domain_add_linear(np, HLWD_NR_IRQS,
179a8db8cf0SGrant Likely 					   &hlwd_irq_domain_ops, io_base);
180bae1d8f1SGrant Likely 	if (!irq_domain) {
181bae1d8f1SGrant Likely 		pr_err("failed to allocate irq_domain\n");
1828d7c0b52SWei Yongjun 		iounmap(io_base);
1839c21025cSAlbert Herranz 		return NULL;
1849c21025cSAlbert Herranz 	}
1859c21025cSAlbert Herranz 
186bae1d8f1SGrant Likely 	return irq_domain;
1879c21025cSAlbert Herranz }
1889c21025cSAlbert Herranz 
hlwd_pic_get_irq(void)1899c21025cSAlbert Herranz unsigned int hlwd_pic_get_irq(void)
1909c21025cSAlbert Herranz {
1912c899658SMarc Zyngier 	unsigned int hwirq = __hlwd_pic_get_irq(hlwd_irq_host);
1922c899658SMarc Zyngier 	return hwirq ? irq_linear_revmap(hlwd_irq_host, hwirq) : 0;
1939c21025cSAlbert Herranz }
1949c21025cSAlbert Herranz 
1959c21025cSAlbert Herranz /*
1969c21025cSAlbert Herranz  * Probe function.
1979c21025cSAlbert Herranz  *
1989c21025cSAlbert Herranz  */
1999c21025cSAlbert Herranz 
hlwd_pic_probe(void)200c0dc225aSNick Child void __init hlwd_pic_probe(void)
2019c21025cSAlbert Herranz {
202bae1d8f1SGrant Likely 	struct irq_domain *host;
2039c21025cSAlbert Herranz 	struct device_node *np;
2049c21025cSAlbert Herranz 	const u32 *interrupts;
2059c21025cSAlbert Herranz 	int cascade_virq;
2069c21025cSAlbert Herranz 
2079c21025cSAlbert Herranz 	for_each_compatible_node(np, NULL, "nintendo,hollywood-pic") {
2089c21025cSAlbert Herranz 		interrupts = of_get_property(np, "interrupts", NULL);
2099c21025cSAlbert Herranz 		if (interrupts) {
2109c21025cSAlbert Herranz 			host = hlwd_pic_init(np);
2119c21025cSAlbert Herranz 			BUG_ON(!host);
2129c21025cSAlbert Herranz 			cascade_virq = irq_of_parse_and_map(np, 0);
213ec775d0eSThomas Gleixner 			irq_set_handler_data(cascade_virq, host);
214ec775d0eSThomas Gleixner 			irq_set_chained_handler(cascade_virq,
2159c21025cSAlbert Herranz 						hlwd_pic_irq_cascade);
2166d166fecSPaul Gortmaker 			hlwd_irq_host = host;
217f6e82647SJulia Lawall 			of_node_put(np);
2189c21025cSAlbert Herranz 			break;
2199c21025cSAlbert Herranz 		}
2209c21025cSAlbert Herranz 	}
2219c21025cSAlbert Herranz }
2229c21025cSAlbert Herranz 
2239c21025cSAlbert Herranz /**
2249c21025cSAlbert Herranz  * hlwd_quiesce() - quiesce hollywood irq controller
2259c21025cSAlbert Herranz  *
2269c21025cSAlbert Herranz  * Mask and ack all interrupt sources.
2279c21025cSAlbert Herranz  *
2289c21025cSAlbert Herranz  */
hlwd_quiesce(void)2299c21025cSAlbert Herranz void hlwd_quiesce(void)
2309c21025cSAlbert Herranz {
2316d166fecSPaul Gortmaker 	void __iomem *io_base = hlwd_irq_host->host_data;
2329c21025cSAlbert Herranz 
2339c21025cSAlbert Herranz 	__hlwd_quiesce(io_base);
2349c21025cSAlbert Herranz }
2359c21025cSAlbert Herranz 
236