xref: /netbsd/sys/arch/macppc/macppc/pic_ohare.c (revision 6550d01e)
1 /*	$NetBSD: pic_ohare.c,v 1.8 2010/12/20 00:25:37 matt 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.8 2010/12/20 00:25:37 matt Exp $");
31 
32 #include "opt_interrupt.h"
33 
34 #include <sys/param.h>
35 #include <sys/malloc.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 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 #define INT_LEVEL_MASK_OHARE	0x1ff00000
72 #define INT_LEVEL_MASK_GC	0x3ff00000
73 
74 int init_ohare(void)
75 {
76 	uint32_t reg[5];
77 	uint32_t obio_base;
78 	uint32_t irq;
79 	int      ohare, ohare2, is_gc = 0;
80 
81 	ohare = OF_finddevice("/bandit/ohare");
82 	if (ohare == -1) {
83 		ohare = OF_finddevice("/bandit/gc");
84 		is_gc = 1;
85 	}
86 
87 
88 	if (OF_getprop(ohare, "assigned-addresses", reg, sizeof(reg)) != 20)
89 		return FALSE;
90 
91 	obio_base = reg[2];
92 	aprint_normal("found %s PIC at %08x\n",
93 	    is_gc ? "Grand Central" : "ohare", obio_base);
94 	setup_ohare(obio_base, is_gc);
95 
96 	/* look for 2nd ohare */
97 	ohare2 = OF_finddevice("/bandit/pci106b,7");
98 	if (ohare2 == -1)
99 		goto done;
100 
101 	if (OF_getprop(ohare2, "assigned-addresses", reg, sizeof(reg)) < 20)
102 		goto done;
103 
104 	if (OF_getprop(ohare2, "AAPL,interrupts", &irq, sizeof(irq)) < 4)
105 		goto done;
106 
107 	obio_base = reg[2];
108 	aprint_normal("found ohare2 PIC at %08x, irq %d\n", obio_base, irq);
109 	setup_ohare2(obio_base, irq);
110 done:
111 	return TRUE;
112 }
113 
114 static struct ohare_ops *
115 setup_ohare(uint32_t addr, int is_gc)
116 {
117 	struct ohare_ops *ohare;
118 	struct pic_ops *pic;
119 	int i;
120 
121 	ohare = malloc(sizeof(struct ohare_ops), M_DEVBUF, M_NOWAIT);
122 	KASSERT(ohare != NULL);
123 	pic = &ohare->pic;
124 
125 	pic->pic_numintrs = OHARE_NIRQ;
126 	pic->pic_cookie = (void *)addr;
127 	pic->pic_enable_irq = ohare_enable_irq;
128 	pic->pic_reenable_irq = ohare_reenable_irq;
129 	pic->pic_disable_irq = ohare_disable_irq;
130 	pic->pic_get_irq = ohare_get_irq;
131 	pic->pic_ack_irq = ohare_ack_irq;
132 	pic->pic_establish_irq = ohare_establish_irq;
133 	pic->pic_finish_setup = NULL;
134 
135 	if (is_gc) {
136 
137 		strcpy(pic->pic_name, "gc");
138 		ohare->level_mask = 0;
139 	} else {
140 
141 		strcpy(pic->pic_name, "ohare");
142 		ohare->level_mask = 0;
143 	}
144 	for (i = 0; i < OHARE_NIRQ; i++)
145 		ohare->priority_masks[i] = 0;
146 	for (i = 0; i < NIPL; i++)
147 		ohare->irqs[i] = 0;
148 	pic_add(pic);
149 	ohare->pending_events = 0;
150 	ohare->enable_mask = 0;
151 	out32rb(INT_ENABLE_REG, 0);
152 	out32rb(INT_CLEAR_REG, 0xffffffff);
153 	return ohare;
154 }
155 
156 static void
157 setup_ohare2(uint32_t addr, int irq)
158 {
159 	struct ohare_ops *pic;
160 
161 	pic = setup_ohare(addr, 0);
162 	strcpy(pic->pic.pic_name, "ohare2");
163 	intr_establish(irq, IST_LEVEL, IPL_NONE, pic_handle_intr, pic);
164 }
165 
166 static void
167 ohare_enable_irq(struct pic_ops *pic, int irq, int type)
168 {
169 	struct ohare_ops *ohare = (struct ohare_ops *)pic;
170 	uint32_t mask = 1 << irq;
171 
172 	ohare->enable_mask |= mask;
173 	out32rb(INT_ENABLE_REG, ohare->enable_mask);
174 }
175 
176 static void
177 ohare_reenable_irq(struct pic_ops *pic, int irq, int type)
178 {
179 	struct ohare_ops *ohare = (struct ohare_ops *)pic;
180 	uint32_t levels;
181 	uint32_t mask = 1 << irq;
182 
183 	ohare->enable_mask |= mask;
184 	out32rb(INT_ENABLE_REG, ohare->enable_mask);
185 	levels = in32rb(INT_LEVEL_REG);
186 	if (levels & mask) {
187 		pic_mark_pending(pic->pic_intrbase + irq);
188 		out32rb(INT_CLEAR_REG, mask);
189 	}
190 }
191 
192 static void
193 ohare_disable_irq(struct pic_ops *pic, int irq)
194 {
195 	struct ohare_ops *ohare = (struct ohare_ops *)pic;
196 	uint32_t mask = 1 << irq;
197 
198 	ohare->enable_mask &= ~mask;
199 	out32rb(INT_ENABLE_REG, ohare->enable_mask);
200 }
201 
202 inline void
203 ohare_read_events(struct ohare_ops *ohare)
204 {
205 	struct pic_ops *pic = &ohare->pic;
206 	uint32_t irqs, events, levels;
207 
208 	irqs = in32rb(INT_STATE_REG);
209 	events = irqs & ~ohare->level_mask;
210 
211 	levels = in32rb(INT_LEVEL_REG) & ohare->enable_mask;
212 	events |= levels & ohare->level_mask;
213 	out32rb(INT_CLEAR_REG, events | irqs);
214 	ohare->pending_events |= events;
215 
216 #if 0
217 	if (events != 0)
218 		aprint_error("%s: ev %08x\n", __func__, events);
219 #endif
220 }
221 
222 static int
223 ohare_get_irq(struct pic_ops *pic, int mode)
224 {
225 	struct ohare_ops *ohare = (struct ohare_ops *)pic;
226 	uint32_t evt;
227 	uint16_t prio;
228 	int bit, mask, lvl;
229 #ifdef OHARE_DEBUG
230 	int bail = 0;
231 #endif
232 
233 	if (ohare->pending_events == 0)
234 		ohare_read_events(ohare);
235 
236 	if (ohare->pending_events == 0)
237 		return 255;
238 
239 	bit = 31 - cntlzw(ohare->pending_events);
240 	mask = 1 << bit;
241 	if ((ohare->pending_events & ~mask) == 0) {
242 
243 		ohare->pending_events = 0;
244 		return bit;
245 	}
246 
247 	/*
248 	 * if we get here we have more than one irq pending so return them
249 	 * according to priority
250 	 */
251 
252 	evt = ohare->pending_events & ~mask;
253 	prio = ohare->priority_masks[bit];
254 	while (evt != 0) {
255 		bit = 31 - cntlzw(evt);
256 		prio |= ohare->priority_masks[bit];
257 		evt &= ~(1 << bit);
258 #ifdef OHARE_DEBUG
259 		bail++;
260 		if (bail > 31)
261 			panic("hanging in ohare_get_irq");
262 #endif
263 	}
264 	lvl = 31 - cntlzw(prio);
265 	evt = ohare->pending_events & ohare->irqs[lvl];
266 
267 	if (evt == 0) {
268 		aprint_verbose("%s: spurious interrupt\n",
269 		    ohare->pic.pic_name);
270 		evt = ohare->pending_events;
271 	}
272 
273 	bit = 31 - cntlzw(evt);
274 	mask = 1 << bit;
275 	ohare->pending_events &= ~mask;
276 	return bit;
277 }
278 
279 static void
280 ohare_ack_irq(struct pic_ops *pic, int irq)
281 {
282 }
283 
284 static void
285 ohare_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
286 {
287 	struct ohare_ops *ohare = (struct ohare_ops *)pic;
288 	uint32_t mask = (1 << irq);
289 	int realpri = min(NIPL, max(0, pri)), i;
290 	uint32_t level = 1 << realpri;
291 
292 	KASSERT((irq >= 0) && (irq < OHARE_NIRQ));
293 
294 	if (type == IST_LEVEL) {
295 
296 		ohare->level_mask |= mask;
297 	} else {
298 
299 		ohare->level_mask &= ~mask;
300 	}
301 	aprint_debug("mask: %08x\n", ohare->level_mask);
302 	ohare->priority_masks[irq] = level;
303 	for (i = 0; i < NIPL; i++)
304 		ohare->irqs[i] = 0;
305 
306 	for (i = 0; i < OHARE_NIRQ; i++) {
307 		if (ohare->priority_masks[i] == 0)
308 			continue;
309 		level = 31 - cntlzw(ohare->priority_masks[i]);
310 		ohare->irqs[level] |= (1 << i);
311 	}
312 }
313