diff options
Diffstat (limited to 'arch/powerpc/platforms/embedded6xx/flipper-pic.c')
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/flipper-pic.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c new file mode 100644 index 000000000000..d5963285e3be --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * arch/powerpc/platforms/embedded6xx/flipper-pic.c | ||
3 | * | ||
4 | * Nintendo GameCube/Wii "Flipper" interrupt controller support. | ||
5 | * Copyright (C) 2004-2009 The GameCube Linux Team | ||
6 | * Copyright (C) 2007,2008,2009 Albert Herranz | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version 2 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | #define DRV_MODULE_NAME "flipper-pic" | ||
15 | #define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <asm/io.h> | ||
22 | |||
23 | #include "flipper-pic.h" | ||
24 | |||
25 | #define FLIPPER_NR_IRQS 32 | ||
26 | |||
27 | /* | ||
28 | * Each interrupt has a corresponding bit in both | ||
29 | * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers. | ||
30 | * | ||
31 | * Enabling/disabling an interrupt line involves setting/clearing | ||
32 | * the corresponding bit in IMR. | ||
33 | * Except for the RSW interrupt, all interrupts get deasserted automatically | ||
34 | * when the source deasserts the interrupt. | ||
35 | */ | ||
36 | #define FLIPPER_ICR 0x00 | ||
37 | #define FLIPPER_ICR_RSS (1<<16) /* reset switch state */ | ||
38 | |||
39 | #define FLIPPER_IMR 0x04 | ||
40 | |||
41 | #define FLIPPER_RESET 0x24 | ||
42 | |||
43 | |||
44 | /* | ||
45 | * IRQ chip hooks. | ||
46 | * | ||
47 | */ | ||
48 | |||
49 | static void flipper_pic_mask_and_ack(unsigned int virq) | ||
50 | { | ||
51 | int irq = virq_to_hw(virq); | ||
52 | void __iomem *io_base = get_irq_chip_data(virq); | ||
53 | u32 mask = 1 << irq; | ||
54 | |||
55 | clrbits32(io_base + FLIPPER_IMR, mask); | ||
56 | /* this is at least needed for RSW */ | ||
57 | out_be32(io_base + FLIPPER_ICR, mask); | ||
58 | } | ||
59 | |||
60 | static void flipper_pic_ack(unsigned int virq) | ||
61 | { | ||
62 | int irq = virq_to_hw(virq); | ||
63 | void __iomem *io_base = get_irq_chip_data(virq); | ||
64 | |||
65 | /* this is at least needed for RSW */ | ||
66 | out_be32(io_base + FLIPPER_ICR, 1 << irq); | ||
67 | } | ||
68 | |||
69 | static void flipper_pic_mask(unsigned int virq) | ||
70 | { | ||
71 | int irq = virq_to_hw(virq); | ||
72 | void __iomem *io_base = get_irq_chip_data(virq); | ||
73 | |||
74 | clrbits32(io_base + FLIPPER_IMR, 1 << irq); | ||
75 | } | ||
76 | |||
77 | static void flipper_pic_unmask(unsigned int virq) | ||
78 | { | ||
79 | int irq = virq_to_hw(virq); | ||
80 | void __iomem *io_base = get_irq_chip_data(virq); | ||
81 | |||
82 | setbits32(io_base + FLIPPER_IMR, 1 << irq); | ||
83 | } | ||
84 | |||
85 | |||
86 | static struct irq_chip flipper_pic = { | ||
87 | .name = "flipper-pic", | ||
88 | .ack = flipper_pic_ack, | ||
89 | .mask_ack = flipper_pic_mask_and_ack, | ||
90 | .mask = flipper_pic_mask, | ||
91 | .unmask = flipper_pic_unmask, | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * IRQ host hooks. | ||
96 | * | ||
97 | */ | ||
98 | |||
99 | static struct irq_host *flipper_irq_host; | ||
100 | |||
101 | static int flipper_pic_map(struct irq_host *h, unsigned int virq, | ||
102 | irq_hw_number_t hwirq) | ||
103 | { | ||
104 | set_irq_chip_data(virq, h->host_data); | ||
105 | get_irq_desc(virq)->status |= IRQ_LEVEL; | ||
106 | set_irq_chip_and_handler(virq, &flipper_pic, handle_level_irq); | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static void flipper_pic_unmap(struct irq_host *h, unsigned int irq) | ||
111 | { | ||
112 | set_irq_chip_data(irq, NULL); | ||
113 | set_irq_chip(irq, NULL); | ||
114 | } | ||
115 | |||
116 | static int flipper_pic_match(struct irq_host *h, struct device_node *np) | ||
117 | { | ||
118 | return 1; | ||
119 | } | ||
120 | |||
121 | |||
122 | static struct irq_host_ops flipper_irq_host_ops = { | ||
123 | .map = flipper_pic_map, | ||
124 | .unmap = flipper_pic_unmap, | ||
125 | .match = flipper_pic_match, | ||
126 | }; | ||
127 | |||
128 | /* | ||
129 | * Platform hooks. | ||
130 | * | ||
131 | */ | ||
132 | |||
133 | static void __flipper_quiesce(void __iomem *io_base) | ||
134 | { | ||
135 | /* mask and ack all IRQs */ | ||
136 | out_be32(io_base + FLIPPER_IMR, 0x00000000); | ||
137 | out_be32(io_base + FLIPPER_ICR, 0xffffffff); | ||
138 | } | ||
139 | |||
140 | struct irq_host * __init flipper_pic_init(struct device_node *np) | ||
141 | { | ||
142 | struct device_node *pi; | ||
143 | struct irq_host *irq_host = NULL; | ||
144 | struct resource res; | ||
145 | void __iomem *io_base; | ||
146 | int retval; | ||
147 | |||
148 | pi = of_get_parent(np); | ||
149 | if (!pi) { | ||
150 | pr_err("no parent found\n"); | ||
151 | goto out; | ||
152 | } | ||
153 | if (!of_device_is_compatible(pi, "nintendo,flipper-pi")) { | ||
154 | pr_err("unexpected parent compatible\n"); | ||
155 | goto out; | ||
156 | } | ||
157 | |||
158 | retval = of_address_to_resource(pi, 0, &res); | ||
159 | if (retval) { | ||
160 | pr_err("no io memory range found\n"); | ||
161 | goto out; | ||
162 | } | ||
163 | io_base = ioremap(res.start, resource_size(&res)); | ||
164 | |||
165 | pr_info("controller at 0x%08x mapped to 0x%p\n", res.start, io_base); | ||
166 | |||
167 | __flipper_quiesce(io_base); | ||
168 | |||
169 | irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, FLIPPER_NR_IRQS, | ||
170 | &flipper_irq_host_ops, -1); | ||
171 | if (!irq_host) { | ||
172 | pr_err("failed to allocate irq_host\n"); | ||
173 | return NULL; | ||
174 | } | ||
175 | |||
176 | irq_host->host_data = io_base; | ||
177 | |||
178 | out: | ||
179 | return irq_host; | ||
180 | } | ||
181 | |||
182 | unsigned int flipper_pic_get_irq(void) | ||
183 | { | ||
184 | void __iomem *io_base = flipper_irq_host->host_data; | ||
185 | int irq; | ||
186 | u32 irq_status; | ||
187 | |||
188 | irq_status = in_be32(io_base + FLIPPER_ICR) & | ||
189 | in_be32(io_base + FLIPPER_IMR); | ||
190 | if (irq_status == 0) | ||
191 | return NO_IRQ; /* no more IRQs pending */ | ||
192 | |||
193 | irq = __ffs(irq_status); | ||
194 | return irq_linear_revmap(flipper_irq_host, irq); | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Probe function. | ||
199 | * | ||
200 | */ | ||
201 | |||
202 | void __init flipper_pic_probe(void) | ||
203 | { | ||
204 | struct device_node *np; | ||
205 | |||
206 | np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-pic"); | ||
207 | BUG_ON(!np); | ||
208 | |||
209 | flipper_irq_host = flipper_pic_init(np); | ||
210 | BUG_ON(!flipper_irq_host); | ||
211 | |||
212 | irq_set_default_host(flipper_irq_host); | ||
213 | |||
214 | of_node_put(np); | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | * Misc functions related to the flipper chipset. | ||
219 | * | ||
220 | */ | ||
221 | |||
222 | /** | ||
223 | * flipper_quiesce() - quiesce flipper irq controller | ||
224 | * | ||
225 | * Mask and ack all interrupt sources. | ||
226 | * | ||
227 | */ | ||
228 | void flipper_quiesce(void) | ||
229 | { | ||
230 | void __iomem *io_base = flipper_irq_host->host_data; | ||
231 | |||
232 | __flipper_quiesce(io_base); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Resets the platform. | ||
237 | */ | ||
238 | void flipper_platform_reset(void) | ||
239 | { | ||
240 | void __iomem *io_base; | ||
241 | |||
242 | if (flipper_irq_host && flipper_irq_host->host_data) { | ||
243 | io_base = flipper_irq_host->host_data; | ||
244 | out_8(io_base + FLIPPER_RESET, 0x00); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * Returns non-zero if the reset button is pressed. | ||
250 | */ | ||
251 | int flipper_is_reset_button_pressed(void) | ||
252 | { | ||
253 | void __iomem *io_base; | ||
254 | u32 icr; | ||
255 | |||
256 | if (flipper_irq_host && flipper_irq_host->host_data) { | ||
257 | io_base = flipper_irq_host->host_data; | ||
258 | icr = in_be32(io_base + FLIPPER_ICR); | ||
259 | return !(icr & FLIPPER_ICR_RSS); | ||
260 | } | ||
261 | return 0; | ||
262 | } | ||
263 | |||