xref: /freebsd/sys/powerpc/powermac/hrowpic.c (revision d056fa046c6a91b90cd98165face0e42a33a5173)
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 	rman_set_rid(rv, *rid);
218 
219 	return (rv);
220 }
221 
222 static int
223 hrowpic_setup_intr(device_t picdev, device_t child, struct resource *res,
224     int flags, driver_intr_t *intr, void *arg, void **cookiep)
225 {
226 	struct  hrowpic_softc *sc;
227 	u_long start;
228 	int error;
229 
230 	sc = device_get_softc(picdev);
231 	start = rman_get_start(res);
232 
233 	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
234 		flags |= INTR_EXCL;
235 
236 	/*
237 	 * We depend here on rman_activate_resource() being idempotent.
238 	 */
239 	error = rman_activate_resource(res);
240 	if (error)
241 		return (error);
242 
243 	error = inthand_add(device_get_nameunit(child), start, intr, arg,
244 	    flags, cookiep);
245 
246 	if (!error) {
247 		/*
248 		 * Record irq request, and enable if h/w has been probed
249 		 */
250 		sc->sc_irq[start] = 1;
251 		if (sc->sc_memr) {
252 			hrowpic_toggle_irq(sc, start, 1);
253 		}
254 	}
255 
256 	return (error);
257 }
258 
259 static int
260 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res,
261     void *ih)
262 {
263 	int     error;
264 
265 	error = rman_deactivate_resource(res);
266 	if (error)
267 		return (error);
268 
269 	error = inthand_remove(rman_get_start(res), ih);
270 
271 	return (error);
272 }
273 
274 static int
275 hrowpic_release_intr(device_t picdev, device_t child, int rid,
276     struct resource *res)
277 {
278 	int     error;
279 
280 	if (rman_get_flags(res) & RF_ACTIVE) {
281 		error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res);
282 		if (error)
283 			return (error);
284 	}
285 
286 	return (rman_release_resource(res));
287 }
288 
289 /*
290  * Interrupt interface
291  */
292 static void
293 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank,
294     u_int32_t val)
295 {
296 	if (bank == HPIC_PRIMARY)
297 		reg += HPIC_1ST_OFFSET;
298 
299 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
300 
301 	/*
302 	 * XXX Issue a read to force the write to complete
303 	 */
304 	bus_space_read_4(sc->sc_bt, sc->sc_bh, reg);
305 }
306 
307 static u_int32_t
308 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank)
309 {
310 	if (bank == HPIC_PRIMARY)
311 		reg += HPIC_1ST_OFFSET;
312 
313 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
314 }
315 
316 static void
317 hrowpic_clear_all(struct hrowpic_softc *sc)
318 {
319 	/*
320 	 * Disable all interrupt sources and clear outstanding interrupts
321 	 */
322 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0);
323 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_PRIMARY, 0xffffffff);
324 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0);
325 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_SECONDARY, 0xffffffff);
326 }
327 
328 static void
329 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable)
330 {
331 	u_int roffset;
332 	u_int rbit;
333 
334 	KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range"));
335 
336 	/*
337 	 * Calculate prim/sec register bank for the IRQ, update soft copy,
338 	 * and enable the IRQ as an interrupt source
339 	 */
340 	roffset = HPIC_INT_TO_BANK(irq);
341 	rbit = HPIC_INT_TO_REGBIT(irq);
342 
343 	if (enable)
344 		sc->sc_softreg[roffset] |= (1 << rbit);
345 	else
346 		sc->sc_softreg[roffset] &= ~(1 << rbit);
347 
348 	hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]);
349 }
350 
351 static void
352 hrowpic_intr(void)
353 {
354 	int irq_lo, irq_hi;
355 	int i;
356 	struct hrowpic_softc *sc;
357 
358 	sc = hpicsoftc;
359 
360 	/*
361 	 * Loop through both interrupt sources until they are empty.
362 	 * XXX simplistic code, far from optimal.
363 	 */
364 	do {
365 		irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY);
366 		if (irq_lo) {
367 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY,
368 			    irq_lo);
369 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
370 				if (irq_lo & (1 << i)) {
371 					/*
372 					 * Disable IRQ and call handler
373 					 */
374 					hrowpic_toggle_irq(sc, i, 0);
375 					intr_handle(i);
376 				}
377 			}
378 
379 		}
380 
381 		irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY);
382 		if (irq_hi) {
383 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY,
384 			    irq_hi);
385 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
386 				if (irq_hi & (1 << i)) {
387 					/*
388 					 * Disable IRQ and call handler
389 					 */
390 					hrowpic_toggle_irq(sc,
391 					    i + HROWPIC_IRQ_REGNUM, 0);
392 					intr_handle(i + HROWPIC_IRQ_REGNUM);
393 				}
394 			}
395 		}
396 	} while (irq_lo && irq_hi);
397 }
398 
399 static void
400 hrowpic_ext_enable_irq(uintptr_t irq)
401 {
402 	hrowpic_toggle_irq(hpicsoftc, irq, 1);
403 }
404 
405 static void
406 hrowpic_ext_disable_irq(uintptr_t irq)
407 {
408 	hrowpic_toggle_irq(hpicsoftc, irq, 0);
409 }
410 
411 
412 /*
413  * MacIO interface
414  */
415 
416 static device_method_t  hrowpic_macio_methods[] = {
417 	/* Device interface */
418 	DEVMETHOD(device_probe,         hrowpic_macio_probe),
419 	DEVMETHOD(device_attach,        hrowpic_macio_attach),
420 
421 	{ 0, 0 },
422 };
423 
424 static driver_t hrowpic_macio_driver = {
425 	"hrowpicmacio",
426 	hrowpic_macio_methods,
427 	0
428 };
429 
430 static devclass_t hrowpic_macio_devclass;
431 
432 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver,
433     hrowpic_macio_devclass, 0, 0);
434 
435 static int
436 hrowpic_macio_probe(device_t dev)
437 {
438         const char *type = ofw_bus_get_type(dev);
439 
440 	/*
441 	 * OpenPIC cells have a type of "open-pic", so this
442 	 * is sufficient to identify a Heathrow cell
443 	 */
444         if (strcmp(type, "interrupt-controller") != 0)
445                 return (ENXIO);
446 
447 	/*
448 	 * The description was already printed out in the nexus
449 	 * probe, so don't do it again here
450 	 */
451         device_set_desc(dev, "Heathrow MacIO interrupt cell");
452 	device_quiet(dev);
453         return (0);
454 }
455 
456 static int
457 hrowpic_macio_attach(device_t dev)
458 {
459 	struct hrowpic_softc *sc = hpicsoftc;
460 	int rid;
461 	int i;
462 
463 	KASSERT(sc != NULL, ("pic not nexus-probed\n"));
464 	sc->sc_maciodev = dev;
465 
466 	rid = 0;
467 	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
468 	    RF_ACTIVE);
469 
470 	if (sc->sc_memr == NULL) {
471 		device_printf(dev, "Could not alloc mem resource!\n");
472 		return (ENXIO);
473 	}
474 
475 	sc->sc_bt = rman_get_bustag(sc->sc_memr);
476 	sc->sc_bh = rman_get_bushandle(sc->sc_memr);
477 
478 	hrowpic_clear_all(sc);
479 
480 	/*
481 	 * Enable all IRQs that were requested before the h/w
482 	 * was probed
483 	 */
484 	for (i = 0; i < HROWPIC_IRQMAX; i++)
485 		if (sc->sc_irq[i]) {
486 			hrowpic_toggle_irq(sc, i, 1);
487 		}
488 
489 	return (0);
490 }
491