hrowpic.c revision 133862
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: head/sys/powerpc/powermac/hrowpic.c 133862 2004-08-16 15:45:27Z marius $
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 */
72static void		hrowpic_identify(driver_t *, device_t);
73static int		hrowpic_probe(device_t);
74static int		hrowpic_attach(device_t);
75
76/*
77 * PIC interface.
78 */
79static struct resource	*hrowpic_allocate_intr(device_t, device_t, int *,
80                            u_long, u_int);
81static int		hrowpic_setup_intr(device_t, device_t,
82                            struct resource *, int, driver_intr_t, void *,
83                            void **);
84static int		hrowpic_teardown_intr(device_t, device_t,
85                            struct resource *, void *);
86static int		hrowpic_release_intr(device_t dev, device_t, int,
87                            struct resource *res);
88
89/*
90 * MacIO interface
91 */
92static int	hrowpic_macio_probe(device_t);
93static int	hrowpic_macio_attach(device_t);
94
95/*
96 * Local routines
97 */
98static void	hrowpic_intr(void);
99static void	hrowpic_ext_enable_irq(uintptr_t);
100static void	hrowpic_ext_disable_irq(uintptr_t);
101static void	hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int);
102
103/*
104 * Interrupt controller softc. There should only be one.
105 */
106static struct hrowpic_softc  *hpicsoftc;
107
108/*
109 * Driver methods.
110 */
111static 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
126static driver_t hrowpic_driver = {
127	"hrowpic",
128	hrowpic_methods,
129	sizeof(struct hrowpic_softc)
130};
131
132static devclass_t hrowpic_devclass;
133
134DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0);
135
136static void
137hrowpic_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
156static int
157hrowpic_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
170static int
171hrowpic_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 */
199static struct resource *
200hrowpic_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
221static int
222hrowpic_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
258static int
259hrowpic_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
273static int
274hrowpic_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 */
291static void
292hrowpic_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
306static u_int32_t
307hrowpic_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
315static void
316hrowpic_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
327static void
328hrowpic_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
350static void
351hrowpic_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
398static void
399hrowpic_ext_enable_irq(uintptr_t irq)
400{
401	hrowpic_toggle_irq(hpicsoftc, irq, 1);
402}
403
404static void
405hrowpic_ext_disable_irq(uintptr_t irq)
406{
407	hrowpic_toggle_irq(hpicsoftc, irq, 0);
408}
409
410
411/*
412 * MacIO interface
413 */
414
415static 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
423static driver_t hrowpic_macio_driver = {
424	"hrowpicmacio",
425	hrowpic_macio_methods,
426	0
427};
428
429static devclass_t hrowpic_macio_devclass;
430
431DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver,
432    hrowpic_macio_devclass, 0, 0);
433
434static int
435hrowpic_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
455static int
456hrowpic_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