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