xref: /freebsd/sys/powerpc/powermac/hrowpic.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*-
2  * Copyright 2003 by Peter Grehan. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 /*
31  *  A driver for the PIC found in the Heathrow/Paddington MacIO chips.
32  * This was superseded by an OpenPIC in the Keylargo and beyond
33  * MacIO versions.
34  *
35  *  The device is initially located in the Open Firmware device tree
36  * in the earliest stage of the nexus probe. However, no device registers
37  * are touched until the actual h/w is probed later on during the
38  * MacIO probe. At that point, any interrupt sources that were allocated
39  * prior to this are activated.
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/module.h>
45 #include <sys/bus.h>
46 #include <sys/conf.h>
47 #include <sys/kernel.h>
48 
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/openfirm.h>
51 
52 #include <machine/bus.h>
53 #include <machine/intr.h>
54 #include <machine/intr_machdep.h>
55 #include <machine/md_var.h>
56 #include <machine/nexusvar.h>
57 #include <machine/pio.h>
58 #include <machine/resource.h>
59 
60 #include <vm/vm.h>
61 #include <vm/pmap.h>
62 
63 #include <sys/rman.h>
64 
65 #include <powerpc/powermac/hrowpicvar.h>
66 
67 #include "pic_if.h"
68 
69 /*
70  * Device interface.
71  */
72 static void		hrowpic_identify(driver_t *, device_t);
73 static int		hrowpic_probe(device_t);
74 static int		hrowpic_attach(device_t);
75 
76 /*
77  * PIC interface.
78  */
79 static struct resource	*hrowpic_allocate_intr(device_t, device_t, int *,
80                             u_long, u_int);
81 static int		hrowpic_setup_intr(device_t, device_t,
82                             struct resource *, int, driver_intr_t, void *,
83                             void **);
84 static int		hrowpic_teardown_intr(device_t, device_t,
85                             struct resource *, void *);
86 static int		hrowpic_release_intr(device_t dev, device_t, int,
87                             struct resource *res);
88 
89 /*
90  * MacIO interface
91  */
92 static int	hrowpic_macio_probe(device_t);
93 static int	hrowpic_macio_attach(device_t);
94 
95 /*
96  * Local routines
97  */
98 static void	hrowpic_intr(void);
99 static void	hrowpic_ext_enable_irq(uintptr_t);
100 static void	hrowpic_ext_disable_irq(uintptr_t);
101 static void	hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int);
102 
103 /*
104  * Interrupt controller softc. There should only be one.
105  */
106 static struct hrowpic_softc  *hpicsoftc;
107 
108 /*
109  * Driver methods.
110  */
111 static device_method_t  hrowpic_methods[] = {
112 	/* Device interface */
113 	DEVMETHOD(device_identify,	hrowpic_identify),
114 	DEVMETHOD(device_probe,         hrowpic_probe),
115 	DEVMETHOD(device_attach,        hrowpic_attach),
116 
117 	/* PIC interface */
118 	DEVMETHOD(pic_allocate_intr,    hrowpic_allocate_intr),
119 	DEVMETHOD(pic_setup_intr,       hrowpic_setup_intr),
120 	DEVMETHOD(pic_teardown_intr,    hrowpic_teardown_intr),
121 	DEVMETHOD(pic_release_intr,     hrowpic_release_intr),
122 
123 	{ 0, 0 }
124 };
125 
126 static driver_t hrowpic_driver = {
127 	"hrowpic",
128 	hrowpic_methods,
129 	sizeof(struct hrowpic_softc)
130 };
131 
132 static devclass_t hrowpic_devclass;
133 
134 DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0);
135 
136 static void
137 hrowpic_identify(driver_t *driver, device_t parent)
138 {
139 	phandle_t chosen, pic;
140 	char type[40];
141 
142 	chosen = OF_finddevice("/chosen");
143 	if (chosen == -1)
144 		return;
145 
146 	if (OF_getprop(chosen, "interrupt-controller", &pic, 4) != 4)
147 		return;
148 
149 	OF_getprop(pic, "compatible", type, sizeof(type));
150 	if (strcmp(type, "heathrow"))
151 		return;
152 
153 	BUS_ADD_CHILD(parent, 0, "hrowpic", 0);
154 }
155 
156 static int
157 hrowpic_probe(device_t dev)
158 {
159 	char    *name;
160 
161 	name = nexus_get_name(dev);
162 
163 	if (strcmp(name, "hrowpic"))
164 		return (ENXIO);
165 
166 	device_set_desc(dev, "Heathrow interrupt controller");
167 	return (0);
168 }
169 
170 static int
171 hrowpic_attach(device_t dev)
172 {
173 	struct hrowpic_softc *sc;
174 
175 	sc = device_get_softc(dev);
176 
177 	sc->sc_rman.rm_type = RMAN_ARRAY;
178 	sc->sc_rman.rm_descr = device_get_nameunit(dev);
179 
180 	if (rman_init(&sc->sc_rman) != 0 ||
181 	    rman_manage_region(&sc->sc_rman, 0, HROWPIC_IRQMAX-1) != 0) {
182 		device_printf(dev, "could not set up resource management");
183 		return (ENXIO);
184         }
185 
186 	nexus_install_intcntlr(dev);
187 	intr_init(hrowpic_intr, HROWPIC_IRQMAX, hrowpic_ext_enable_irq,
188 	    hrowpic_ext_disable_irq);
189 
190 	KASSERT(hpicsoftc == NULL, ("hrowpic: h/w already probed"));
191 	hpicsoftc = sc;
192 
193 	return (0);
194 }
195 
196 /*
197  * PIC interface
198  */
199 static struct resource *
200 hrowpic_allocate_intr(device_t picdev, device_t child, int *rid, u_long intr,
201     u_int flags)
202 {
203 	struct  hrowpic_softc *sc;
204 	struct  resource *rv;
205 	int     needactivate;
206 
207 	sc = device_get_softc(picdev);
208 	needactivate = flags & RF_ACTIVE;
209 	flags &= ~RF_ACTIVE;
210 
211 	rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child);
212 	if (rv == NULL) {
213 		device_printf(picdev, "interrupt reservation failed for %s\n",
214 		    device_get_nameunit(child));
215 		return (NULL);
216 	}
217 
218 	return (rv);
219 }
220 
221 static int
222 hrowpic_setup_intr(device_t picdev, device_t child, struct resource *res,
223     int flags, driver_intr_t *intr, void *arg, void **cookiep)
224 {
225 	struct  hrowpic_softc *sc;
226 	u_long start;
227 	int error;
228 
229 	sc = device_get_softc(picdev);
230 	start = rman_get_start(res);
231 
232 	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
233 		flags |= INTR_EXCL;
234 
235 	/*
236 	 * We depend here on rman_activate_resource() being idempotent.
237 	 */
238 	error = rman_activate_resource(res);
239 	if (error)
240 		return (error);
241 
242 	error = inthand_add(device_get_nameunit(child), start, intr, arg,
243 	    flags, cookiep);
244 
245 	if (!error) {
246 		/*
247 		 * Record irq request, and enable if h/w has been probed
248 		 */
249 		sc->sc_irq[start] = 1;
250 		if (sc->sc_memr) {
251 			hrowpic_toggle_irq(sc, start, 1);
252 		}
253 	}
254 
255 	return (error);
256 }
257 
258 static int
259 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res,
260     void *ih)
261 {
262 	int     error;
263 
264 	error = rman_deactivate_resource(res);
265 	if (error)
266 		return (error);
267 
268 	error = inthand_remove(rman_get_start(res), ih);
269 
270 	return (error);
271 }
272 
273 static int
274 hrowpic_release_intr(device_t picdev, device_t child, int rid,
275     struct resource *res)
276 {
277 	int     error;
278 
279 	if (rman_get_flags(res) & RF_ACTIVE) {
280 		error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res);
281 		if (error)
282 			return (error);
283 	}
284 
285 	return (rman_release_resource(res));
286 }
287 
288 /*
289  * Interrupt interface
290  */
291 static void
292 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank,
293     u_int32_t val)
294 {
295 	if (bank == HPIC_PRIMARY)
296 		reg += HPIC_1ST_OFFSET;
297 
298 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
299 
300 	/*
301 	 * XXX Issue a read to force the write to complete
302 	 */
303 	bus_space_read_4(sc->sc_bt, sc->sc_bh, reg);
304 }
305 
306 static u_int32_t
307 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank)
308 {
309 	if (bank == HPIC_PRIMARY)
310 		reg += HPIC_1ST_OFFSET;
311 
312 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
313 }
314 
315 static void
316 hrowpic_clear_all(struct hrowpic_softc *sc)
317 {
318 	/*
319 	 * Disable all interrupt sources and clear outstanding interrupts
320 	 */
321 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0);
322 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_PRIMARY, 0xffffffff);
323 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0);
324 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_SECONDARY, 0xffffffff);
325 }
326 
327 static void
328 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable)
329 {
330 	u_int roffset;
331 	u_int rbit;
332 
333 	KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range"));
334 
335 	/*
336 	 * Calculate prim/sec register bank for the IRQ, update soft copy,
337 	 * and enable the IRQ as an interrupt source
338 	 */
339 	roffset = HPIC_INT_TO_BANK(irq);
340 	rbit = HPIC_INT_TO_REGBIT(irq);
341 
342 	if (enable)
343 		sc->sc_softreg[roffset] |= (1 << rbit);
344 	else
345 		sc->sc_softreg[roffset] &= ~(1 << rbit);
346 
347 	hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]);
348 }
349 
350 static void
351 hrowpic_intr(void)
352 {
353 	int irq_lo, irq_hi;
354 	int i;
355 	struct hrowpic_softc *sc;
356 
357 	sc = hpicsoftc;
358 
359 	/*
360 	 * Loop through both interrupt sources until they are empty.
361 	 * XXX simplistic code, far from optimal.
362 	 */
363 	do {
364 		irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY);
365 		if (irq_lo) {
366 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY,
367 			    irq_lo);
368 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
369 				if (irq_lo & (1 << i)) {
370 					/*
371 					 * Disable IRQ and call handler
372 					 */
373 					hrowpic_toggle_irq(sc, i, 0);
374 					intr_handle(i);
375 				}
376 			}
377 
378 		}
379 
380 		irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY);
381 		if (irq_hi) {
382 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY,
383 			    irq_hi);
384 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
385 				if (irq_hi & (1 << i)) {
386 					/*
387 					 * Disable IRQ and call handler
388 					 */
389 					hrowpic_toggle_irq(sc,
390 					    i + HROWPIC_IRQ_REGNUM, 0);
391 					intr_handle(i + HROWPIC_IRQ_REGNUM);
392 				}
393 			}
394 		}
395 	} while (irq_lo && irq_hi);
396 }
397 
398 static void
399 hrowpic_ext_enable_irq(uintptr_t irq)
400 {
401 	hrowpic_toggle_irq(hpicsoftc, irq, 1);
402 }
403 
404 static void
405 hrowpic_ext_disable_irq(uintptr_t irq)
406 {
407 	hrowpic_toggle_irq(hpicsoftc, irq, 0);
408 }
409 
410 
411 /*
412  * MacIO interface
413  */
414 
415 static device_method_t  hrowpic_macio_methods[] = {
416 	/* Device interface */
417 	DEVMETHOD(device_probe,         hrowpic_macio_probe),
418 	DEVMETHOD(device_attach,        hrowpic_macio_attach),
419 
420 	{ 0, 0 },
421 };
422 
423 static driver_t hrowpic_macio_driver = {
424 	"hrowpicmacio",
425 	hrowpic_macio_methods,
426 	0
427 };
428 
429 static devclass_t hrowpic_macio_devclass;
430 
431 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver,
432     hrowpic_macio_devclass, 0, 0);
433 
434 static int
435 hrowpic_macio_probe(device_t dev)
436 {
437         const char *type = ofw_bus_get_type(dev);
438 
439 	/*
440 	 * OpenPIC cells have a type of "open-pic", so this
441 	 * is sufficient to identify a Heathrow cell
442 	 */
443         if (strcmp(type, "interrupt-controller") != 0)
444                 return (ENXIO);
445 
446 	/*
447 	 * The description was already printed out in the nexus
448 	 * probe, so don't do it again here
449 	 */
450         device_set_desc(dev, "Heathrow MacIO interrupt cell");
451 	device_quiet(dev);
452         return (0);
453 }
454 
455 static int
456 hrowpic_macio_attach(device_t dev)
457 {
458 	struct hrowpic_softc *sc = hpicsoftc;
459 	int rid;
460 	int i;
461 
462 	KASSERT(sc != NULL, ("pic not nexus-probed\n"));
463 	sc->sc_maciodev = dev;
464 
465 	rid = 0;
466 	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
467 	    RF_ACTIVE);
468 
469 	if (sc->sc_memr == NULL) {
470 		device_printf(dev, "Could not alloc mem resource!\n");
471 		return (ENXIO);
472 	}
473 
474 	sc->sc_bt = rman_get_bustag(sc->sc_memr);
475 	sc->sc_bh = rman_get_bushandle(sc->sc_memr);
476 
477 	hrowpic_clear_all(sc);
478 
479 	/*
480 	 * Enable all IRQs that were requested before the h/w
481 	 * was probed
482 	 */
483 	for (i = 0; i < HROWPIC_IRQMAX; i++)
484 		if (sc->sc_irq[i]) {
485 			hrowpic_toggle_irq(sc, i, 1);
486 		}
487 
488 	return (0);
489 }
490