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