xref: /openbmc/linux/arch/powerpc/platforms/embedded6xx/flipper-pic.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2028ee972SAlbert Herranz /*
3028ee972SAlbert Herranz  * arch/powerpc/platforms/embedded6xx/flipper-pic.c
4028ee972SAlbert Herranz  *
5028ee972SAlbert Herranz  * Nintendo GameCube/Wii "Flipper" interrupt controller support.
6028ee972SAlbert Herranz  * Copyright (C) 2004-2009 The GameCube Linux Team
7028ee972SAlbert Herranz  * Copyright (C) 2007,2008,2009 Albert Herranz
8028ee972SAlbert Herranz  */
9028ee972SAlbert Herranz #define DRV_MODULE_NAME "flipper-pic"
10028ee972SAlbert Herranz #define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
11028ee972SAlbert Herranz 
12028ee972SAlbert Herranz #include <linux/kernel.h>
13028ee972SAlbert Herranz #include <linux/init.h>
14028ee972SAlbert Herranz #include <linux/irq.h>
1513a9a5d1SMarc Zyngier #include <linux/irqdomain.h>
16028ee972SAlbert Herranz #include <linux/of.h>
1726a2056eSRob Herring #include <linux/of_address.h>
18028ee972SAlbert Herranz #include <asm/io.h>
19028ee972SAlbert Herranz 
20028ee972SAlbert Herranz #include "flipper-pic.h"
21028ee972SAlbert Herranz 
22028ee972SAlbert Herranz #define FLIPPER_NR_IRQS		32
23028ee972SAlbert Herranz 
24028ee972SAlbert Herranz /*
25028ee972SAlbert Herranz  * Each interrupt has a corresponding bit in both
26028ee972SAlbert Herranz  * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.
27028ee972SAlbert Herranz  *
28028ee972SAlbert Herranz  * Enabling/disabling an interrupt line involves setting/clearing
29028ee972SAlbert Herranz  * the corresponding bit in IMR.
30028ee972SAlbert Herranz  * Except for the RSW interrupt, all interrupts get deasserted automatically
31028ee972SAlbert Herranz  * when the source deasserts the interrupt.
32028ee972SAlbert Herranz  */
33028ee972SAlbert Herranz #define FLIPPER_ICR		0x00
34028ee972SAlbert Herranz #define FLIPPER_ICR_RSS		(1<<16) /* reset switch state */
35028ee972SAlbert Herranz 
36028ee972SAlbert Herranz #define FLIPPER_IMR		0x04
37028ee972SAlbert Herranz 
38028ee972SAlbert Herranz #define FLIPPER_RESET		0x24
39028ee972SAlbert Herranz 
40028ee972SAlbert Herranz 
41028ee972SAlbert Herranz /*
42028ee972SAlbert Herranz  * IRQ chip hooks.
43028ee972SAlbert Herranz  *
44028ee972SAlbert Herranz  */
45028ee972SAlbert Herranz 
flipper_pic_mask_and_ack(struct irq_data * d)460bf8878eSLennert Buytenhek static void flipper_pic_mask_and_ack(struct irq_data *d)
47028ee972SAlbert Herranz {
48476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
490bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
50028ee972SAlbert Herranz 	u32 mask = 1 << irq;
51028ee972SAlbert Herranz 
52028ee972SAlbert Herranz 	clrbits32(io_base + FLIPPER_IMR, mask);
53028ee972SAlbert Herranz 	/* this is at least needed for RSW */
54028ee972SAlbert Herranz 	out_be32(io_base + FLIPPER_ICR, mask);
55028ee972SAlbert Herranz }
56028ee972SAlbert Herranz 
flipper_pic_ack(struct irq_data * d)570bf8878eSLennert Buytenhek static void flipper_pic_ack(struct irq_data *d)
58028ee972SAlbert Herranz {
59476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
600bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
61028ee972SAlbert Herranz 
62028ee972SAlbert Herranz 	/* this is at least needed for RSW */
63028ee972SAlbert Herranz 	out_be32(io_base + FLIPPER_ICR, 1 << irq);
64028ee972SAlbert Herranz }
65028ee972SAlbert Herranz 
flipper_pic_mask(struct irq_data * d)660bf8878eSLennert Buytenhek static void flipper_pic_mask(struct irq_data *d)
67028ee972SAlbert Herranz {
68476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
690bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
70028ee972SAlbert Herranz 
71028ee972SAlbert Herranz 	clrbits32(io_base + FLIPPER_IMR, 1 << irq);
72028ee972SAlbert Herranz }
73028ee972SAlbert Herranz 
flipper_pic_unmask(struct irq_data * d)740bf8878eSLennert Buytenhek static void flipper_pic_unmask(struct irq_data *d)
75028ee972SAlbert Herranz {
76476eb491SGrant Likely 	int irq = irqd_to_hwirq(d);
770bf8878eSLennert Buytenhek 	void __iomem *io_base = irq_data_get_irq_chip_data(d);
78028ee972SAlbert Herranz 
79028ee972SAlbert Herranz 	setbits32(io_base + FLIPPER_IMR, 1 << irq);
80028ee972SAlbert Herranz }
81028ee972SAlbert Herranz 
82028ee972SAlbert Herranz 
83028ee972SAlbert Herranz static struct irq_chip flipper_pic = {
84028ee972SAlbert Herranz 	.name		= "flipper-pic",
850bf8878eSLennert Buytenhek 	.irq_ack	= flipper_pic_ack,
860bf8878eSLennert Buytenhek 	.irq_mask_ack	= flipper_pic_mask_and_ack,
870bf8878eSLennert Buytenhek 	.irq_mask	= flipper_pic_mask,
880bf8878eSLennert Buytenhek 	.irq_unmask	= flipper_pic_unmask,
89028ee972SAlbert Herranz };
90028ee972SAlbert Herranz 
91028ee972SAlbert Herranz /*
92028ee972SAlbert Herranz  * IRQ host hooks.
93028ee972SAlbert Herranz  *
94028ee972SAlbert Herranz  */
95028ee972SAlbert Herranz 
96bae1d8f1SGrant Likely static struct irq_domain *flipper_irq_host;
97028ee972SAlbert Herranz 
flipper_pic_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hwirq)98bae1d8f1SGrant Likely static int flipper_pic_map(struct irq_domain *h, unsigned int virq,
99028ee972SAlbert Herranz 			   irq_hw_number_t hwirq)
100028ee972SAlbert Herranz {
101ec775d0eSThomas Gleixner 	irq_set_chip_data(virq, h->host_data);
10298488db9SThomas Gleixner 	irq_set_status_flags(virq, IRQ_LEVEL);
103ec775d0eSThomas Gleixner 	irq_set_chip_and_handler(virq, &flipper_pic, handle_level_irq);
104028ee972SAlbert Herranz 	return 0;
105028ee972SAlbert Herranz }
106028ee972SAlbert Herranz 
1079f70b8ebSGrant Likely static const struct irq_domain_ops flipper_irq_domain_ops = {
108028ee972SAlbert Herranz 	.map = flipper_pic_map,
109028ee972SAlbert Herranz };
110028ee972SAlbert Herranz 
111028ee972SAlbert Herranz /*
112028ee972SAlbert Herranz  * Platform hooks.
113028ee972SAlbert Herranz  *
114028ee972SAlbert Herranz  */
115028ee972SAlbert Herranz 
__flipper_quiesce(void __iomem * io_base)116028ee972SAlbert Herranz static void __flipper_quiesce(void __iomem *io_base)
117028ee972SAlbert Herranz {
118028ee972SAlbert Herranz 	/* mask and ack all IRQs */
119028ee972SAlbert Herranz 	out_be32(io_base + FLIPPER_IMR, 0x00000000);
120028ee972SAlbert Herranz 	out_be32(io_base + FLIPPER_ICR, 0xffffffff);
121028ee972SAlbert Herranz }
122028ee972SAlbert Herranz 
flipper_pic_init(struct device_node * np)1238b51e679SMathieu Malaterre static struct irq_domain * __init flipper_pic_init(struct device_node *np)
124028ee972SAlbert Herranz {
125028ee972SAlbert Herranz 	struct device_node *pi;
126bae1d8f1SGrant Likely 	struct irq_domain *irq_domain = NULL;
127028ee972SAlbert Herranz 	struct resource res;
128028ee972SAlbert Herranz 	void __iomem *io_base;
129028ee972SAlbert Herranz 	int retval;
130028ee972SAlbert Herranz 
131028ee972SAlbert Herranz 	pi = of_get_parent(np);
132028ee972SAlbert Herranz 	if (!pi) {
133028ee972SAlbert Herranz 		pr_err("no parent found\n");
134028ee972SAlbert Herranz 		goto out;
135028ee972SAlbert Herranz 	}
136028ee972SAlbert Herranz 	if (!of_device_is_compatible(pi, "nintendo,flipper-pi")) {
137028ee972SAlbert Herranz 		pr_err("unexpected parent compatible\n");
138028ee972SAlbert Herranz 		goto out;
139028ee972SAlbert Herranz 	}
140028ee972SAlbert Herranz 
141028ee972SAlbert Herranz 	retval = of_address_to_resource(pi, 0, &res);
142028ee972SAlbert Herranz 	if (retval) {
143028ee972SAlbert Herranz 		pr_err("no io memory range found\n");
144028ee972SAlbert Herranz 		goto out;
145028ee972SAlbert Herranz 	}
146028ee972SAlbert Herranz 	io_base = ioremap(res.start, resource_size(&res));
147028ee972SAlbert Herranz 
148*7b69600dSRandy Dunlap 	pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);
149028ee972SAlbert Herranz 
150028ee972SAlbert Herranz 	__flipper_quiesce(io_base);
151028ee972SAlbert Herranz 
152a8db8cf0SGrant Likely 	irq_domain = irq_domain_add_linear(np, FLIPPER_NR_IRQS,
153a8db8cf0SGrant Likely 				  &flipper_irq_domain_ops, io_base);
154bae1d8f1SGrant Likely 	if (!irq_domain) {
155bae1d8f1SGrant Likely 		pr_err("failed to allocate irq_domain\n");
156028ee972SAlbert Herranz 		return NULL;
157028ee972SAlbert Herranz 	}
158028ee972SAlbert Herranz 
159028ee972SAlbert Herranz out:
160bae1d8f1SGrant Likely 	return irq_domain;
161028ee972SAlbert Herranz }
162028ee972SAlbert Herranz 
flipper_pic_get_irq(void)163028ee972SAlbert Herranz unsigned int flipper_pic_get_irq(void)
164028ee972SAlbert Herranz {
1656d166fecSPaul Gortmaker 	void __iomem *io_base = flipper_irq_host->host_data;
166028ee972SAlbert Herranz 	int irq;
167028ee972SAlbert Herranz 	u32 irq_status;
168028ee972SAlbert Herranz 
169028ee972SAlbert Herranz 	irq_status = in_be32(io_base + FLIPPER_ICR) &
170028ee972SAlbert Herranz 		     in_be32(io_base + FLIPPER_IMR);
171028ee972SAlbert Herranz 	if (irq_status == 0)
172ef24ba70SMichael Ellerman 		return 0;	/* no more IRQs pending */
173028ee972SAlbert Herranz 
174028ee972SAlbert Herranz 	irq = __ffs(irq_status);
1756d166fecSPaul Gortmaker 	return irq_linear_revmap(flipper_irq_host, irq);
176028ee972SAlbert Herranz }
177028ee972SAlbert Herranz 
178028ee972SAlbert Herranz /*
179028ee972SAlbert Herranz  * Probe function.
180028ee972SAlbert Herranz  *
181028ee972SAlbert Herranz  */
182028ee972SAlbert Herranz 
flipper_pic_probe(void)183028ee972SAlbert Herranz void __init flipper_pic_probe(void)
184028ee972SAlbert Herranz {
185028ee972SAlbert Herranz 	struct device_node *np;
186028ee972SAlbert Herranz 
187028ee972SAlbert Herranz 	np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-pic");
188028ee972SAlbert Herranz 	BUG_ON(!np);
189028ee972SAlbert Herranz 
1906d166fecSPaul Gortmaker 	flipper_irq_host = flipper_pic_init(np);
191028ee972SAlbert Herranz 	BUG_ON(!flipper_irq_host);
192028ee972SAlbert Herranz 
193028ee972SAlbert Herranz 	irq_set_default_host(flipper_irq_host);
194028ee972SAlbert Herranz 
195028ee972SAlbert Herranz 	of_node_put(np);
196028ee972SAlbert Herranz }
197028ee972SAlbert Herranz 
198028ee972SAlbert Herranz /*
199028ee972SAlbert Herranz  * Misc functions related to the flipper chipset.
200028ee972SAlbert Herranz  *
201028ee972SAlbert Herranz  */
202028ee972SAlbert Herranz 
203028ee972SAlbert Herranz /**
204028ee972SAlbert Herranz  * flipper_quiesce() - quiesce flipper irq controller
205028ee972SAlbert Herranz  *
206028ee972SAlbert Herranz  * Mask and ack all interrupt sources.
207028ee972SAlbert Herranz  *
208028ee972SAlbert Herranz  */
flipper_quiesce(void)209028ee972SAlbert Herranz void flipper_quiesce(void)
210028ee972SAlbert Herranz {
211028ee972SAlbert Herranz 	void __iomem *io_base = flipper_irq_host->host_data;
212028ee972SAlbert Herranz 
213028ee972SAlbert Herranz 	__flipper_quiesce(io_base);
214028ee972SAlbert Herranz }
215028ee972SAlbert Herranz 
216028ee972SAlbert Herranz /*
217028ee972SAlbert Herranz  * Resets the platform.
218028ee972SAlbert Herranz  */
flipper_platform_reset(void)219028ee972SAlbert Herranz void flipper_platform_reset(void)
220028ee972SAlbert Herranz {
221028ee972SAlbert Herranz 	void __iomem *io_base;
222028ee972SAlbert Herranz 
223028ee972SAlbert Herranz 	if (flipper_irq_host && flipper_irq_host->host_data) {
224028ee972SAlbert Herranz 		io_base = flipper_irq_host->host_data;
225028ee972SAlbert Herranz 		out_8(io_base + FLIPPER_RESET, 0x00);
226028ee972SAlbert Herranz 	}
227028ee972SAlbert Herranz }
228028ee972SAlbert Herranz 
229028ee972SAlbert Herranz /*
230028ee972SAlbert Herranz  * Returns non-zero if the reset button is pressed.
231028ee972SAlbert Herranz  */
flipper_is_reset_button_pressed(void)232028ee972SAlbert Herranz int flipper_is_reset_button_pressed(void)
233028ee972SAlbert Herranz {
234028ee972SAlbert Herranz 	void __iomem *io_base;
235028ee972SAlbert Herranz 	u32 icr;
236028ee972SAlbert Herranz 
237028ee972SAlbert Herranz 	if (flipper_irq_host && flipper_irq_host->host_data) {
238028ee972SAlbert Herranz 		io_base = flipper_irq_host->host_data;
239028ee972SAlbert Herranz 		icr = in_be32(io_base + FLIPPER_ICR);
240028ee972SAlbert Herranz 		return !(icr & FLIPPER_ICR_RSS);
241028ee972SAlbert Herranz 	}
242028ee972SAlbert Herranz 	return 0;
243028ee972SAlbert Herranz }
244028ee972SAlbert Herranz 
245