1 /* $NetBSD: pic_ohare.c,v 1.17 2021/03/05 07:15:53 rin Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pic_ohare.c,v 1.17 2021/03/05 07:15:53 rin Exp $");
31
32 #include "opt_interrupt.h"
33
34 #include <sys/param.h>
35 #include <sys/kmem.h>
36 #include <sys/kernel.h>
37
38 #include <machine/pio.h>
39
40 #include <dev/ofw/openfirm.h>
41
42 #include <machine/autoconf.h>
43 #include <arch/powerpc/pic/picvar.h>
44
45 static void ohare_enable_irq(struct pic_ops *, int, int);
46 static void ohare_reenable_irq(struct pic_ops *, int, int);
47 static void ohare_disable_irq(struct pic_ops *, int);
48 static int ohare_get_irq(struct pic_ops *, int);
49 static void ohare_ack_irq(struct pic_ops *, int);
50 static void ohare_establish_irq(struct pic_ops *, int, int, int);
51
52 #define OHARE_NIRQ 32
53
54 struct ohare_ops {
55 struct pic_ops pic;
56 uint32_t pending_events;
57 uint32_t enable_mask;
58 uint32_t level_mask;
59 uint32_t irqs[NIPL]; /* per priority level */
60 uint32_t priority_masks[OHARE_NIRQ]; /* per IRQ */
61 };
62
63 static struct ohare_ops *setup_ohare(uint32_t, int);
64 static void setup_ohare2(uint32_t, int);
65 static inline void ohare_read_events(struct ohare_ops *);
66
67 #define INT_STATE_REG ((uint32_t)pic->pic_cookie + 0x20)
68 #define INT_ENABLE_REG ((uint32_t)pic->pic_cookie + 0x24)
69 #define INT_CLEAR_REG ((uint32_t)pic->pic_cookie + 0x28)
70 #define INT_LEVEL_REG ((uint32_t)pic->pic_cookie + 0x2c)
71
init_ohare(void)72 int init_ohare(void)
73 {
74 uint32_t reg[5];
75 uint32_t obio_base;
76 uint32_t irq;
77 int ohare, ohare2, is_gc = 0;
78
79 ohare = OF_finddevice("/bandit/ohare");
80 if (ohare == -1) {
81 ohare = OF_finddevice("/bandit/gc");
82 is_gc = 1;
83 }
84
85
86 if (OF_getprop(ohare, "assigned-addresses", reg, sizeof(reg)) != 20)
87 return FALSE;
88
89 obio_base = reg[2];
90 aprint_normal("found %s PIC at %08x\n",
91 is_gc ? "Grand Central" : "ohare", obio_base);
92 setup_ohare(obio_base, is_gc);
93
94 /* look for 2nd ohare */
95 ohare2 = OF_finddevice("/bandit/pci106b,7");
96 if (ohare2 == -1)
97 goto done;
98
99 if (OF_getprop(ohare2, "assigned-addresses", reg, sizeof(reg)) < 20)
100 goto done;
101
102 if (OF_getprop(ohare2, "AAPL,interrupts", &irq, sizeof(irq)) < 4)
103 goto done;
104
105 obio_base = reg[2];
106 aprint_normal("found ohare2 PIC at %08x, irq %d\n", obio_base, irq);
107 setup_ohare2(obio_base, irq);
108 done:
109 return TRUE;
110 }
111
112 static struct ohare_ops *
setup_ohare(uint32_t addr,int is_gc)113 setup_ohare(uint32_t addr, int is_gc)
114 {
115 struct ohare_ops *ohare;
116 struct pic_ops *pic;
117 int i;
118
119 ohare = kmem_zalloc(sizeof(struct ohare_ops), KM_SLEEP);
120 pic = &ohare->pic;
121
122 pic->pic_numintrs = OHARE_NIRQ;
123 pic->pic_cookie = (void *)addr;
124 pic->pic_enable_irq = ohare_enable_irq;
125 pic->pic_reenable_irq = ohare_reenable_irq;
126 pic->pic_disable_irq = ohare_disable_irq;
127 pic->pic_get_irq = ohare_get_irq;
128 pic->pic_ack_irq = ohare_ack_irq;
129 pic->pic_establish_irq = ohare_establish_irq;
130 pic->pic_finish_setup = NULL;
131
132 if (is_gc) {
133
134 strcpy(pic->pic_name, "gc");
135 } else {
136
137 strcpy(pic->pic_name, "ohare");
138 }
139 ohare->level_mask = 0;
140
141 for (i = 0; i < OHARE_NIRQ; i++)
142 ohare->priority_masks[i] = 0;
143 for (i = 0; i < NIPL; i++)
144 ohare->irqs[i] = 0;
145 pic_add(pic);
146 ohare->pending_events = 0;
147 ohare->enable_mask = 0;
148 out32rb(INT_ENABLE_REG, 0);
149 out32rb(INT_CLEAR_REG, 0xffffffff);
150 return ohare;
151 }
152
153 static void
setup_ohare2(uint32_t addr,int irq)154 setup_ohare2(uint32_t addr, int irq)
155 {
156 struct ohare_ops *pic;
157
158 pic = setup_ohare(addr, 0);
159 strcpy(pic->pic.pic_name, "ohare2");
160 intr_establish_xname(irq, IST_LEVEL, IPL_HIGH, pic_handle_intr, pic,
161 "ohara2");
162 }
163
164 static void
ohare_enable_irq(struct pic_ops * pic,int irq,int type)165 ohare_enable_irq(struct pic_ops *pic, int irq, int type)
166 {
167 struct ohare_ops *ohare = (struct ohare_ops *)pic;
168 uint32_t mask = 1 << irq;
169
170 ohare->enable_mask |= mask;
171 out32rb(INT_ENABLE_REG, ohare->enable_mask);
172 }
173
174 static void
ohare_reenable_irq(struct pic_ops * pic,int irq,int type)175 ohare_reenable_irq(struct pic_ops *pic, int irq, int type)
176 {
177 struct ohare_ops *ohare = (struct ohare_ops *)pic;
178 uint32_t levels;
179 uint32_t mask = 1 << irq;
180
181 ohare->enable_mask |= mask;
182 out32rb(INT_ENABLE_REG, ohare->enable_mask);
183 levels = in32rb(INT_STATE_REG);
184 if (levels & mask) {
185 pic_mark_pending(pic->pic_intrbase + irq);
186 out32rb(INT_CLEAR_REG, mask);
187 }
188 }
189
190 static void
ohare_disable_irq(struct pic_ops * pic,int irq)191 ohare_disable_irq(struct pic_ops *pic, int irq)
192 {
193 struct ohare_ops *ohare = (struct ohare_ops *)pic;
194 uint32_t mask = 1 << irq;
195
196 ohare->enable_mask &= ~mask;
197 out32rb(INT_ENABLE_REG, ohare->enable_mask);
198 }
199
200 static inline void
ohare_read_events(struct ohare_ops * ohare)201 ohare_read_events(struct ohare_ops *ohare)
202 {
203 struct pic_ops *pic = &ohare->pic;
204 uint32_t irqs, events, levels;
205
206 irqs = in32rb(INT_STATE_REG);
207 events = irqs & ~ohare->level_mask;
208
209 levels = in32rb(INT_LEVEL_REG) & ohare->enable_mask;
210 events |= levels & ohare->level_mask;
211 out32rb(INT_CLEAR_REG, events | irqs);
212 ohare->pending_events |= events;
213
214 #if 0
215 if (events != 0)
216 aprint_error("%s: ev %08x\n", __func__, events);
217 #endif
218 }
219
220 static int
ohare_get_irq(struct pic_ops * pic,int mode)221 ohare_get_irq(struct pic_ops *pic, int mode)
222 {
223 struct ohare_ops *ohare = (struct ohare_ops *)pic;
224 uint32_t evt;
225 uint16_t prio;
226 int bit, mask, lvl;
227 #ifdef OHARE_DEBUG
228 int bail = 0;
229 #endif
230
231 if (ohare->pending_events == 0)
232 ohare_read_events(ohare);
233
234 if (ohare->pending_events == 0)
235 return 255;
236
237 bit = 31 - __builtin_clz(ohare->pending_events);
238 mask = 1 << bit;
239
240 if ((ohare->pending_events & ~mask) == 0) {
241
242 ohare->pending_events = 0;
243 return bit;
244 }
245
246 /*
247 * if we get here we have more than one irq pending so return them
248 * according to priority
249 */
250
251 evt = ohare->pending_events & ~mask;
252 prio = ohare->priority_masks[bit];
253 while (evt != 0) {
254 bit = 31 - __builtin_clz(evt);
255 prio |= ohare->priority_masks[bit];
256 evt &= ~(1 << bit);
257 #ifdef OHARE_DEBUG
258 bail++;
259 if (bail > 31)
260 panic("hanging in ohare_get_irq");
261 #endif
262 }
263 lvl = 31 - __builtin_clz(prio);
264 evt = ohare->pending_events & ohare->irqs[lvl];
265
266 if (evt == 0) {
267 #ifdef OHARE_DEBUG
268 aprint_verbose("%s: spurious interrupt\n",
269 ohare->pic.pic_name);
270 printf("levels: %08x\n", in32rb(INT_LEVEL_REG));
271 printf("states: %08x\n", in32rb(INT_STATE_REG));
272 printf("enable: %08x\n", in32rb(INT_ENABLE_REG));
273 printf("events: %08x\n", ohare->pending_events);
274 #endif
275 evt = ohare->pending_events;
276 }
277
278 bit = 31 - __builtin_clz(evt);
279 mask = 1 << bit;
280 ohare->pending_events &= ~mask;
281 return bit;
282 }
283
284 static void
ohare_ack_irq(struct pic_ops * pic,int irq)285 ohare_ack_irq(struct pic_ops *pic, int irq)
286 {
287 }
288
289 static void
ohare_establish_irq(struct pic_ops * pic,int irq,int type,int pri)290 ohare_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
291 {
292 struct ohare_ops *ohare = (struct ohare_ops *)pic;
293 uint32_t mask = (1 << irq);
294 int realpri = uimin(NIPL, uimax(0, pri)), i;
295 uint32_t level = 1 << realpri;
296
297 KASSERT((irq >= 0) && (irq < OHARE_NIRQ));
298
299 if (type == IST_LEVEL) {
300
301 ohare->level_mask |= mask;
302 } else {
303
304 ohare->level_mask &= ~mask;
305 }
306 aprint_debug("mask: %08x\n", ohare->level_mask);
307 ohare->priority_masks[irq] = level;
308 for (i = 0; i < NIPL; i++)
309 ohare->irqs[i] = 0;
310
311 for (i = 0; i < OHARE_NIRQ; i++) {
312 if (ohare->priority_masks[i] == 0)
313 continue;
314 level = 31 - __builtin_clz(ohare->priority_masks[i]);
315 ohare->irqs[level] |= (1 << i);
316 }
317 }
318