xref: /freebsd/sys/powerpc/powermac/hrowpic.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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 OpenFirmware 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 #define __RMAN_RESOURCE_VISIBLE
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/module.h>
46 #include <sys/bus.h>
47 #include <sys/conf.h>
48 #include <sys/kernel.h>
49 
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/maciovar.h>
66 #include <powerpc/powermac/hrowpicvar.h>
67 
68 #include "pic_if.h"
69 
70 /*
71  * Device interface.
72  */
73 static void		hrowpic_identify(driver_t *, device_t);
74 static int		hrowpic_probe(device_t);
75 static int		hrowpic_attach(device_t);
76 
77 /*
78  * PIC interface.
79  */
80 static struct resource	*hrowpic_allocate_intr(device_t, device_t, int *,
81                             u_long, u_int);
82 static int		hrowpic_setup_intr(device_t, device_t,
83                             struct resource *, int, driver_intr_t, void *,
84                             void **);
85 static int		hrowpic_teardown_intr(device_t, device_t,
86                             struct resource *, void *);
87 static int		hrowpic_release_intr(device_t dev, device_t, int,
88                             struct resource *res);
89 
90 /*
91  * MacIO interface
92  */
93 static int	hrowpic_macio_probe(device_t);
94 static int	hrowpic_macio_attach(device_t);
95 
96 /*
97  * Local routines
98  */
99 static void	hrowpic_intr(void);
100 static void	hrowpic_ext_enable_irq(uintptr_t);
101 static void	hrowpic_ext_disable_irq(uintptr_t);
102 static void	hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int);
103 
104 /*
105  * Interrupt controller softc. There should only be one.
106  */
107 static struct hrowpic_softc  *hpicsoftc;
108 
109 /*
110  * Driver methods.
111  */
112 static device_method_t  hrowpic_methods[] = {
113 	/* Device interface */
114 	DEVMETHOD(device_identify,	hrowpic_identify),
115 	DEVMETHOD(device_probe,         hrowpic_probe),
116 	DEVMETHOD(device_attach,        hrowpic_attach),
117 
118 	/* PIC interface */
119 	DEVMETHOD(pic_allocate_intr,    hrowpic_allocate_intr),
120 	DEVMETHOD(pic_setup_intr,       hrowpic_setup_intr),
121 	DEVMETHOD(pic_teardown_intr,    hrowpic_teardown_intr),
122 	DEVMETHOD(pic_release_intr,     hrowpic_release_intr),
123 
124 	{ 0, 0 }
125 };
126 
127 static driver_t hrowpic_driver = {
128 	"hrowpic",
129 	hrowpic_methods,
130 	sizeof(struct hrowpic_softc)
131 };
132 
133 static devclass_t hrowpic_devclass;
134 
135 DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0);
136 
137 static void
138 hrowpic_identify(driver_t *driver, device_t parent)
139 {
140 	phandle_t chosen, pic;
141 	char type[40];
142 
143 	chosen = OF_finddevice("/chosen");
144 	if (chosen == -1)
145 		return;
146 
147 	if (OF_getprop(chosen, "interrupt-controller", &pic, 4) != 4)
148 		return;
149 
150 	OF_getprop(pic, "compatible", type, sizeof(type));
151 	if (strcmp(type, "heathrow"))
152 		return;
153 
154 	BUS_ADD_CHILD(parent, 0, "hrowpic", 0);
155 }
156 
157 static int
158 hrowpic_probe(device_t dev)
159 {
160 	char    *name;
161 
162 	name = nexus_get_name(dev);
163 
164 	if (strcmp(name, "hrowpic"))
165 		return (ENXIO);
166 
167 	device_set_desc(dev, "Heathrow interrupt controller");
168 	return (0);
169 }
170 
171 static int
172 hrowpic_attach(device_t dev)
173 {
174 	struct hrowpic_softc *sc;
175 
176 	sc = device_get_softc(dev);
177 
178 	sc->sc_rman.rm_type = RMAN_ARRAY;
179 	sc->sc_rman.rm_descr = device_get_nameunit(dev);
180 
181 	if (rman_init(&sc->sc_rman) != 0 ||
182 	    rman_manage_region(&sc->sc_rman, 0, HROWPIC_IRQMAX-1) != 0) {
183 		device_printf(dev, "could not set up resource management");
184 		return (ENXIO);
185         }
186 
187 	nexus_install_intcntlr(dev);
188 	intr_init(hrowpic_intr, HROWPIC_IRQMAX, hrowpic_ext_enable_irq,
189 	    hrowpic_ext_disable_irq);
190 
191 	KASSERT(hpicsoftc == NULL, ("hrowpic: h/w already probed"));
192 	hpicsoftc = sc;
193 
194 	return (0);
195 }
196 
197 /*
198  * PIC interface
199  */
200 static struct resource *
201 hrowpic_allocate_intr(device_t picdev, device_t child, int *rid, u_long intr,
202     u_int flags)
203 {
204 	struct  hrowpic_softc *sc;
205 	struct  resource *rv;
206 	int     needactivate;
207 
208 	sc = device_get_softc(picdev);
209 	needactivate = flags & RF_ACTIVE;
210 	flags &= ~RF_ACTIVE;
211 
212 	rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child);
213 	if (rv == NULL) {
214 		device_printf(picdev, "interrupt reservation failed for %s\n",
215 		    device_get_nameunit(child));
216 		return (NULL);
217 	}
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 	int error;
228 
229 	sc = device_get_softc(picdev);
230 
231 	if ((res->r_flags & RF_SHAREABLE) == 0)
232 		flags |= INTR_EXCL;
233 
234 	/*
235 	 * We depend here on rman_activate_resource() being idempotent.
236 	 */
237 	error = rman_activate_resource(res);
238 	if (error)
239 		return (error);
240 
241 	error = inthand_add(device_get_nameunit(child), res->r_start, intr,
242 	    arg, flags, cookiep);
243 
244 	if (!error) {
245 		/*
246 		 * Record irq request, and enable if h/w has been probed
247 		 */
248 		sc->sc_irq[res->r_start] = 1;
249 		if (sc->sc_memr) {
250 			hrowpic_toggle_irq(sc, res->r_start, 1);
251 		}
252 	}
253 
254 	return (error);
255 }
256 
257 static int
258 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res,
259     void *ih)
260 {
261 	int     error;
262 
263 	error = rman_deactivate_resource(res);
264 	if (error)
265 		return (error);
266 
267 	error = inthand_remove(res->r_start, ih);
268 
269 	return (error);
270 }
271 
272 static int
273 hrowpic_release_intr(device_t picdev, device_t child, int rid,
274     struct resource *res)
275 {
276 	int     error;
277 
278 	if (rman_get_flags(res) & RF_ACTIVE) {
279 		error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res);
280 		if (error)
281 			return (error);
282 	}
283 
284 	return (rman_release_resource(res));
285 }
286 
287 /*
288  * Interrupt interface
289  */
290 static void
291 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank,
292     u_int32_t val)
293 {
294 	if (bank == HPIC_PRIMARY)
295 		reg += HPIC_1ST_OFFSET;
296 
297 	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
298 
299 	/*
300 	 * XXX Issue a read to force the write to complete
301 	 */
302 	bus_space_read_4(sc->sc_bt, sc->sc_bh, reg);
303 }
304 
305 static u_int32_t
306 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank)
307 {
308 	if (bank == HPIC_PRIMARY)
309 		reg += HPIC_1ST_OFFSET;
310 
311 	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
312 }
313 
314 static void
315 hrowpic_clear_all(struct hrowpic_softc *sc)
316 {
317 	/*
318 	 * Disable all interrupt sources and clear outstanding interrupts
319 	 */
320 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0);
321 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_PRIMARY, 0xffffffff);
322 	hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0);
323 	hrowpic_write_reg(sc, HPIC_CLEAR,  HPIC_SECONDARY, 0xffffffff);
324 }
325 
326 static void
327 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable)
328 {
329 	u_int roffset;
330 	u_int rbit;
331 
332 	KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range"));
333 
334 	/*
335 	 * Calculate prim/sec register bank for the IRQ, update soft copy,
336 	 * and enable the IRQ as an interrupt source
337 	 */
338 	roffset = HPIC_INT_TO_BANK(irq);
339 	rbit = HPIC_INT_TO_REGBIT(irq);
340 
341 	if (enable)
342 		sc->sc_softreg[roffset] |= (1 << rbit);
343 	else
344 		sc->sc_softreg[roffset] &= ~(1 << rbit);
345 
346 	hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]);
347 }
348 
349 static void
350 hrowpic_intr(void)
351 {
352 	int irq_lo, irq_hi;
353 	int i;
354 	struct hrowpic_softc *sc;
355 
356 	sc = hpicsoftc;
357 
358 	/*
359 	 * Loop through both interrupt sources until they are empty.
360 	 * XXX simplistic code, far from optimal.
361 	 */
362 	do {
363 		irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY);
364 		if (irq_lo) {
365 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY,
366 			    irq_lo);
367 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
368 				if (irq_lo & (1 << i)) {
369 					/*
370 					 * Disable IRQ and call handler
371 					 */
372 					hrowpic_toggle_irq(sc, i, 0);
373 					intr_handle(i);
374 				}
375 			}
376 
377 		}
378 
379 		irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY);
380 		if (irq_hi) {
381 			hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY,
382 			    irq_hi);
383 			for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) {
384 				if (irq_hi & (1 << i)) {
385 					/*
386 					 * Disable IRQ and call handler
387 					 */
388 					hrowpic_toggle_irq(sc,
389 					    i + HROWPIC_IRQ_REGNUM, 0);
390 					intr_handle(i + HROWPIC_IRQ_REGNUM);
391 				}
392 			}
393 		}
394 	} while (irq_lo && irq_hi);
395 }
396 
397 static void
398 hrowpic_ext_enable_irq(uintptr_t irq)
399 {
400 	hrowpic_toggle_irq(hpicsoftc, irq, 1);
401 }
402 
403 static void
404 hrowpic_ext_disable_irq(uintptr_t irq)
405 {
406 	hrowpic_toggle_irq(hpicsoftc, irq, 0);
407 }
408 
409 
410 /*
411  * MacIO interface
412  */
413 
414 static device_method_t  hrowpic_macio_methods[] = {
415 	/* Device interface */
416 	DEVMETHOD(device_probe,         hrowpic_macio_probe),
417 	DEVMETHOD(device_attach,        hrowpic_macio_attach),
418 
419 	{ 0, 0 },
420 };
421 
422 static driver_t hrowpic_macio_driver = {
423 	"hrowpicmacio",
424 	hrowpic_macio_methods,
425 	0
426 };
427 
428 static devclass_t hrowpic_macio_devclass;
429 
430 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver,
431     hrowpic_macio_devclass, 0, 0);
432 
433 static int
434 hrowpic_macio_probe(device_t dev)
435 {
436         char *type = macio_get_devtype(dev);
437 
438 	/*
439 	 * OpenPIC cells have a type of "open-pic", so this
440 	 * is sufficient to identify a Heathrow cell
441 	 */
442         if (strcmp(type, "interrupt-controller") != 0)
443                 return (ENXIO);
444 
445 	/*
446 	 * The description was already printed out in the nexus
447 	 * probe, so don't do it again here
448 	 */
449         device_set_desc(dev, "Heathrow MacIO interrupt cell");
450 	device_quiet(dev);
451         return (0);
452 }
453 
454 static int
455 hrowpic_macio_attach(device_t dev)
456 {
457 	struct hrowpic_softc *sc = hpicsoftc;
458 	int rid;
459 	int i;
460 
461 	KASSERT(sc != NULL, ("pic not nexus-probed\n"));
462 	sc->sc_maciodev = dev;
463 
464 	rid = 0;
465 	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
466 	    RF_ACTIVE);
467 
468 	if (sc->sc_memr == NULL) {
469 		device_printf(dev, "Could not alloc mem resource!\n");
470 		return (ENXIO);
471 	}
472 
473 	sc->sc_bt = rman_get_bustag(sc->sc_memr);
474 	sc->sc_bh = rman_get_bushandle(sc->sc_memr);
475 
476 	hrowpic_clear_all(sc);
477 
478 	/*
479 	 * Enable all IRQs that were requested before the h/w
480 	 * was probed
481 	 */
482 	for (i = 0; i < HROWPIC_IRQMAX; i++)
483 		if (sc->sc_irq[i]) {
484 			hrowpic_toggle_irq(sc, i, 1);
485 		}
486 
487 	return (0);
488 }
489