xref: /netbsd/sys/arch/macppc/macppc/pic_ohare.c (revision 1a75059b)
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