xref: /freebsd/sys/powerpc/powerpc/openpic.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (C) 2002 Benno Rice.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #define __RMAN_RESOURCE_VISIBLE
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34 
35 #include <machine/bus.h>
36 #include <machine/intr.h>
37 #include <machine/intr_machdep.h>
38 #include <machine/md_var.h>
39 #include <machine/pio.h>
40 #include <machine/resource.h>
41 
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44 
45 #include <sys/rman.h>
46 
47 #include <machine/openpicreg.h>
48 #include <machine/openpicvar.h>
49 
50 #include "pic_if.h"
51 
52 /*
53  * Local routines
54  */
55 static u_int		openpic_read(struct openpic_softc *, int);
56 static void		openpic_write(struct openpic_softc *, int, u_int);
57 static int		openpic_read_irq(struct openpic_softc *, int);
58 static void		openpic_eoi(struct openpic_softc *, int);
59 static void		openpic_enable_irq(struct openpic_softc *, int, int);
60 static void		openpic_disable_irq(struct openpic_softc *, int);
61 static void		openpic_set_priority(struct openpic_softc *, int, int);
62 static void		openpic_intr(void);
63 static void		openpic_ext_enable_irq(uintptr_t);
64 static void		openpic_ext_disable_irq(uintptr_t);
65 
66 /* XXX This limits us to one openpic */
67 static struct	openpic_softc *openpic_softc;
68 
69 /*
70  * Called at nexus-probe time to allow interrupts to be enabled by
71  * devices that are probed before the OpenPIC h/w is probed.
72  */
73 int
74 openpic_early_attach(device_t dev)
75 {
76 	struct		openpic_softc *sc;
77 
78 	sc = device_get_softc(dev);
79 	openpic_softc = sc;
80 
81 	sc->sc_rman.rm_type = RMAN_ARRAY;
82 	sc->sc_rman.rm_descr = device_get_nameunit(dev);
83 
84 	if (rman_init(&sc->sc_rman) != 0 ||
85 	    rman_manage_region(&sc->sc_rman, 0, OPENPIC_IRQMAX-1) != 0) {
86 		device_printf(dev, "could not set up resource management");
87 		return (ENXIO);
88         }
89 
90 	intr_init(openpic_intr, OPENPIC_IRQMAX, openpic_ext_enable_irq,
91 	    openpic_ext_disable_irq);
92 
93 	sc->sc_early_done = 1;
94 
95 	return (0);
96 }
97 
98 int
99 openpic_attach(device_t dev)
100 {
101 	struct openpic_softc *sc;
102 	u_int     irq;
103 	u_int32_t x;
104 
105 	sc = device_get_softc(dev);
106 	sc->sc_hwprobed = 1;
107 
108 	if (!sc->sc_early_done)
109 		openpic_early_attach(dev);
110 
111 	x = openpic_read(sc, OPENPIC_FEATURE);
112 	switch (x & OPENPIC_FEATURE_VERSION_MASK) {
113 	case 1:
114 		sc->sc_version = "1.0";
115 		break;
116 	case 2:
117 		sc->sc_version = "1.2";
118 		break;
119 	case 3:
120 		sc->sc_version = "1.3";
121 		break;
122 	default:
123 		sc->sc_version = "unknown";
124 		break;
125 	}
126 
127 	sc->sc_ncpu = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >>
128 	    OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1;
129 	sc->sc_nirq = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >>
130 	    OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1;
131 
132 	/*
133 	 * PSIM seems to report 1 too many IRQs
134 	 */
135 	if (sc->sc_psim)
136 		sc->sc_nirq--;
137 
138 	if (bootverbose)
139 		device_printf(dev,
140 		    "Version %s, supports %d CPUs and %d irqs\n",
141 		    sc->sc_version, sc->sc_ncpu, sc->sc_nirq);
142 
143 	/* disable all interrupts */
144 	for (irq = 0; irq < sc->sc_nirq; irq++)
145 		openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK);
146 
147 	openpic_set_priority(sc, 0, 15);
148 
149 	/* we don't need 8259 passthrough mode */
150 	x = openpic_read(sc, OPENPIC_CONFIG);
151 	x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE;
152 	openpic_write(sc, OPENPIC_CONFIG, x);
153 
154 	/* send all interrupts to cpu 0 */
155 	for (irq = 0; irq < sc->sc_nirq; irq++)
156 		openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0);
157 
158 	for (irq = 0; irq < sc->sc_nirq; irq++) {
159 		x = irq;
160 		x |= OPENPIC_IMASK;
161 		x |= OPENPIC_POLARITY_POSITIVE;
162 		x |= OPENPIC_SENSE_LEVEL;
163 		x |= 8 << OPENPIC_PRIORITY_SHIFT;
164 		openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
165 	}
166 
167 	/* XXX IPI */
168 	/* XXX set spurious intr vector */
169 
170 	openpic_set_priority(sc, 0, 0);
171 
172 	/* clear all pending interrupts */
173 	for (irq = 0; irq < sc->sc_nirq; irq++) {
174 		openpic_read_irq(sc, 0);
175 		openpic_eoi(sc, 0);
176 	}
177 
178 	/* enable pre-h/w reserved irqs, disable all others */
179 	for (irq = 0; irq < sc->sc_nirq; irq++)
180 		if (sc->sc_irqrsv[irq])
181 			openpic_enable_irq(sc, irq, IST_LEVEL);
182 		else
183 			openpic_disable_irq(sc, irq);
184 
185 	return (0);
186 }
187 
188 /*
189  * PIC interface
190  */
191 
192 struct resource *
193 openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr,
194     u_int flags)
195 {
196 	struct	openpic_softc *sc;
197 	struct	resource *rv;
198 	int	needactivate;
199 
200 	sc = device_get_softc(dev);
201 	needactivate = flags & RF_ACTIVE;
202 	flags &= ~RF_ACTIVE;
203 
204 	if (sc->sc_hwprobed && (intr > sc->sc_nirq)) {
205 		device_printf(dev, "interrupt reservation %ld out of range\n",
206 		    intr);
207 		return (NULL);
208 	}
209 
210 	rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child);
211 	if (rv == NULL) {
212 		device_printf(dev, "interrupt reservation failed for %s\n",
213 		    device_get_nameunit(child));
214 		return (NULL);
215 	}
216 
217 	if (needactivate) {
218 		if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) {
219 			device_printf(dev,
220 			    "resource activation failed for %s\n",
221 			    device_get_nameunit(child));
222 			rman_release_resource(rv);
223 			return (NULL);
224 		}
225 	}
226 
227 	return (rv);
228 }
229 
230 int
231 openpic_setup_intr(device_t dev, device_t child, struct resource *res,
232     int flags, driver_intr_t *intr, void *arg, void **cookiep)
233 {
234 	struct	openpic_softc *sc;
235 	int	error;
236 
237 	sc = device_get_softc(dev);
238 
239 	if (res == NULL) {
240 		device_printf(dev, "null interrupt resource from %s\n",
241 		    device_get_nameunit(child));
242 		return (EINVAL);
243 	}
244 
245 	if ((res->r_flags & RF_SHAREABLE) == 0)
246 		flags |= INTR_EXCL;
247 
248 	/*
249 	 * We depend here on rman_activate_resource() being idempotent.
250 	 */
251 	error = rman_activate_resource(res);
252 	if (error)
253 		return (error);
254 
255 	error = inthand_add(device_get_nameunit(child), res->r_start, intr,
256 	    arg, flags, cookiep);
257 
258 	if (sc->sc_hwprobed)
259 		openpic_enable_irq(sc, res->r_start, IST_LEVEL);
260 	else
261 		sc->sc_irqrsv[res->r_start] = 1;
262 
263 	return (error);
264 }
265 
266 int
267 openpic_teardown_intr(device_t dev, device_t child, struct resource *res,
268     void *ih)
269 {
270 	int	error;
271 
272 	error = rman_deactivate_resource(res);
273 	if (error)
274 		return (error);
275 
276 	error = inthand_remove(res->r_start, ih);
277 
278 	return (error);
279 }
280 
281 int
282 openpic_release_intr(device_t dev, device_t child, int rid,
283     struct resource *res)
284 {
285 	int	error;
286 
287 	if (rman_get_flags(res) & RF_ACTIVE) {
288 		error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res);
289 		if (error)
290 			return (error);
291 	}
292 
293 	return (rman_release_resource(res));
294 }
295 
296 /*
297  * Local routines
298  */
299 
300 static u_int
301 openpic_read(struct openpic_softc *sc, int reg)
302 {
303 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
304 }
305 
306 static void
307 openpic_write(struct openpic_softc *sc, int reg, u_int val)
308 {
309 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
310 }
311 
312 static int
313 openpic_read_irq(struct openpic_softc *sc, int cpu)
314 {
315 	return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
316 }
317 
318 static void
319 openpic_eoi(struct openpic_softc *sc, int cpu)
320 {
321 	openpic_write(sc, OPENPIC_EOI(cpu), 0);
322 }
323 
324 static void
325 openpic_enable_irq(struct openpic_softc *sc, int irq, int type)
326 {
327 	u_int	x;
328 
329 	x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq));
330 	x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE);
331 	if (type == IST_LEVEL)
332 		x |= OPENPIC_SENSE_LEVEL;
333 	else
334 		x |= OPENPIC_SENSE_EDGE;
335 	openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
336 }
337 
338 static void
339 openpic_disable_irq(struct openpic_softc *sc, int irq)
340 {
341 	u_int	x;
342 
343 	x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq));
344 	x |= OPENPIC_IMASK;
345 	openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
346 }
347 
348 static void
349 openpic_set_priority(struct openpic_softc *sc, int cpu, int pri)
350 {
351 	u_int	x;
352 
353 	x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu));
354 	x &= ~OPENPIC_CPU_PRIORITY_MASK;
355 	x |= pri;
356 	openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x);
357 }
358 
359 static void
360 openpic_intr(void)
361 {
362 	struct openpic_softc *sc;
363 	int		irq;
364 	u_int32_t	msr;
365 
366 	sc = openpic_softc;
367 	msr = mfmsr();
368 
369 	irq = openpic_read_irq(sc, 0);
370 	if (irq == 255) {
371 		return;
372 	}
373 
374 start:
375 	openpic_disable_irq(sc, irq);
376 	/*mtmsr(msr | PSL_EE);*/
377 
378 	/* do the interrupt thang */
379 	intr_handle(irq);
380 
381 	mtmsr(msr);
382 
383 	openpic_eoi(sc, 0);
384 
385 	irq = openpic_read_irq(sc, 0);
386 	if (irq != 255)
387 		goto start;
388 }
389 
390 static void
391 openpic_ext_enable_irq(uintptr_t irq)
392 {
393 	if (!openpic_softc->sc_hwprobed)
394 		return;
395 
396 	openpic_enable_irq(openpic_softc, irq, IST_LEVEL);
397 }
398 
399 static void
400 openpic_ext_disable_irq(uintptr_t irq)
401 {
402 	if (!openpic_softc->sc_hwprobed)
403 		return;
404 
405 	openpic_disable_irq(openpic_softc, irq);
406 }
407