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