openpic.c revision 99723
1/*
2 * Copyright (C) 2002 Benno Rice.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * $FreeBSD: head/sys/powerpc/powerpc/openpic.c 99723 2002-07-10 09:46:24Z benno $
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bus.h>
31#include <sys/conf.h>
32#include <sys/kernel.h>
33
34#include <dev/ofw/openfirm.h>
35#include <dev/ofw/ofw_pci.h>
36
37#include <dev/pci/pcivar.h>
38#include <dev/pci/pcireg.h>
39
40#include <machine/bus.h>
41#include <machine/intr.h>
42#include <machine/intr_machdep.h>
43#include <machine/md_var.h>
44#include <machine/nexusvar.h>
45#include <machine/pio.h>
46#include <machine/resource.h>
47
48#include <vm/vm.h>
49#include <vm/pmap.h>
50
51#include <sys/rman.h>
52
53#include <machine/openpicreg.h>
54#include <machine/openpicvar.h>
55
56#include "pic_if.h"
57
58/*
59 * Device interface.
60 */
61static int		openpic_probe(device_t);
62static int		openpic_attach(device_t);
63
64/*
65 * PIC interface.
66 */
67static struct resource	*openpic_allocate_intr(device_t, device_t, int *,
68			    u_long, u_int);
69static int		openpic_setup_intr(device_t, device_t,
70			    struct resource *, int, driver_intr_t, void *,
71			    void **);
72static int		openpic_teardown_intr(device_t, device_t,
73			    struct resource *, void *);
74static int		openpic_release_intr(device_t dev, device_t, int,
75			    struct resource *res);
76
77/*
78 * Local routines
79 */
80static u_int		openpic_read(struct openpic_softc *, int);
81static void		openpic_write(struct openpic_softc *, int, u_int);
82static int		openpic_read_irq(struct openpic_softc *, int);
83static void		openpic_eoi(struct openpic_softc *, int);
84static void		openpic_enable_irq(struct openpic_softc *, int, int);
85static void		openpic_disable_irq(struct openpic_softc *, int);
86static void		openpic_set_priority(struct openpic_softc *, int, int);
87static void		openpic_intr(void);
88static void		irq_enable(int);
89static void		irq_disable(int);
90
91/*
92 * Driver methods.
93 */
94static device_method_t	openpic_methods[] = {
95	/* Device interface */
96	DEVMETHOD(device_probe,		openpic_probe),
97	DEVMETHOD(device_attach,	openpic_attach),
98
99	/* PIC interface */
100	DEVMETHOD(pic_allocate_intr,	openpic_allocate_intr),
101	DEVMETHOD(pic_setup_intr,	openpic_setup_intr),
102	DEVMETHOD(pic_teardown_intr,	openpic_teardown_intr),
103	DEVMETHOD(pic_release_intr,	openpic_release_intr),
104
105	{ 0, 0 }
106};
107
108static driver_t	openpic_driver = {
109	"openpic",
110	openpic_methods,
111	sizeof(struct openpic_softc)
112};
113
114static devclass_t	openpic_devclass;
115
116DRIVER_MODULE(openpic, nexus, openpic_driver, openpic_devclass, 0, 0);
117
118static struct	openpic_softc *softc;	/* XXX This limits us to one openpic */
119
120/*
121 * Device interface
122 */
123
124static int
125openpic_probe(device_t dev)
126{
127	struct		openpic_softc *sc;
128	phandle_t	node, parent;
129	char		*type;
130	u_int32_t	reg[5], val;
131	vm_offset_t	macio_base;
132
133	sc = device_get_softc(dev);
134	node = nexus_get_node(dev);
135	type = nexus_get_device_type(dev);
136
137	if (type == NULL)
138		return (ENXIO);
139
140	if (strcmp(type, "open-pic") != 0)
141		return (ENXIO);
142
143	parent = OF_parent(node);
144	if (OF_getprop(parent, "assigned-addresses", reg, sizeof(reg)) < 20)
145		return (ENXIO);
146	macio_base = (vm_offset_t)reg[2];
147
148	if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
149		return (ENXIO);
150
151	sc->sc_base = (vm_offset_t)pmap_mapdev(macio_base + reg[0],
152	    OPENPIC_SIZE);
153
154	val = openpic_read(sc, OPENPIC_FEATURE);
155	switch (val & OPENPIC_FEATURE_VERSION_MASK) {
156	case 1:
157		sc->sc_version = "1.0";
158		break;
159	case 2:
160		sc->sc_version = "1.2";
161		break;
162	case 3:
163		sc->sc_version = "1.3";
164		break;
165	default:
166		sc->sc_version = "unknown";
167		break;
168	}
169
170	sc->sc_ncpu = ((val & OPENPIC_FEATURE_LAST_CPU_MASK) >>
171	    OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1;
172	sc->sc_nirq = ((val & OPENPIC_FEATURE_LAST_IRQ_MASK) >>
173	    OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1;
174
175	device_set_desc(dev, "OpenPIC interrupt controller");
176	return (0);
177}
178
179static int
180openpic_attach(device_t dev)
181{
182	struct		openpic_softc *sc;
183	u_int32_t	irq, x;
184
185	sc = device_get_softc(dev);
186	softc = sc;
187
188	device_printf(dev,
189	    "Version %s, supports up to %d CPUs and up to %d irqs\n",
190	    sc->sc_version, sc->sc_ncpu, sc->sc_nirq);
191
192	sc->sc_rman.rm_type = RMAN_ARRAY;
193	sc->sc_rman.rm_descr = device_get_nameunit(dev);
194
195	if (rman_init(&sc->sc_rman) != 0 ||
196	    rman_manage_region(&sc->sc_rman, 0, sc->sc_nirq - 1) != 0) {
197		device_printf(dev, "could not set up resource management");
198		return (ENXIO);
199	}
200
201	/* disable all interrupts */
202	for (irq = 0; irq < 256; irq++)
203		openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK);
204
205	openpic_set_priority(sc, 0, 15);
206
207	/* we don't need 8259 passthrough mode */
208	x = openpic_read(sc, OPENPIC_CONFIG);
209	x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE;
210	openpic_write(sc, OPENPIC_CONFIG, x);
211
212	/* send all interrupts to cpu 0 */
213	for (irq = 0; irq < sc->sc_nirq; irq++)
214		openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0);
215
216	for (irq = 0; irq < sc->sc_nirq; irq++) {
217		x = irq;
218		x |= OPENPIC_IMASK;
219		x |= OPENPIC_POLARITY_POSITIVE;
220		x |= OPENPIC_SENSE_LEVEL;
221		x |= 8 << OPENPIC_PRIORITY_SHIFT;
222		openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
223	}
224
225	/* XXX IPI */
226	/* XXX set spurious intr vector */
227
228	openpic_set_priority(sc, 0, 0);
229
230	/* clear all pending interrupts */
231	for (irq = 0; irq < 256; irq++) {
232		openpic_read_irq(sc, 0);
233		openpic_eoi(sc, 0);
234	}
235
236	for (irq = 0; irq < sc->sc_nirq; irq++)
237		openpic_disable_irq(sc, irq);
238
239	intr_init(openpic_intr, sc->sc_nirq, irq_enable, irq_disable);
240
241	return (0);
242}
243
244/*
245 * PIC interface
246 */
247
248static struct resource *
249openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr,
250    u_int flags)
251{
252	struct	openpic_softc *sc;
253	struct	resource *rv;
254	int	needactivate;
255
256	sc = device_get_softc(dev);
257	needactivate = flags & RF_ACTIVE;
258	flags &= ~RF_ACTIVE;
259
260	rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child);
261	if (rv == NULL) {
262		device_printf(dev, "interrupt reservation failed for %s\n",
263		    device_get_nameunit(child));
264		return (NULL);
265	}
266
267	if (needactivate) {
268		if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) {
269			device_printf(dev,
270			    "resource activation failed for %s\n",
271			    device_get_nameunit(child));
272			rman_release_resource(rv);
273			return (NULL);
274		}
275	}
276
277	return (rv);
278}
279
280static int
281openpic_setup_intr(device_t dev, device_t child, struct resource *res,
282    int flags, driver_intr_t *intr, void *arg, void **cookiep)
283{
284	struct	openpic_softc *sc;
285	int	error;
286
287	sc = device_get_softc(dev);
288
289	if (res == NULL) {
290		device_printf(dev, "null interrupt resource from %s\n",
291		    device_get_nameunit(child));
292		return (EINVAL);
293	}
294
295	if ((res->r_flags & RF_SHAREABLE) == 0)
296		flags |= INTR_EXCL;
297
298	/*
299	 * We depend here on rman_activate_resource() being idempotent.
300	 */
301	error = rman_activate_resource(res);
302	if (error)
303		return (error);
304
305	error = inthand_add(device_get_nameunit(child), res->r_start, intr,
306	    arg, flags, cookiep);
307	openpic_enable_irq(sc, res->r_start, IST_LEVEL);
308
309	return (error);
310}
311
312static int
313openpic_teardown_intr(device_t dev, device_t child, struct resource *res,
314    void *ih)
315{
316	int	error;
317
318	error = rman_deactivate_resource(res);
319	if (error)
320		return (error);
321
322	error = inthand_remove(res->r_start, ih);
323
324	return (error);
325}
326
327static int
328openpic_release_intr(device_t dev, device_t child, int rid,
329    struct resource *res)
330{
331	int	error;
332
333	if (rman_get_flags(res) & RF_ACTIVE) {
334		error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res);
335		if (error)
336			return (error);
337	}
338
339	return (rman_release_resource(res));
340}
341
342/*
343 * Local routines
344 */
345
346static u_int
347openpic_read(struct openpic_softc *sc, int reg)
348{
349	volatile unsigned char *addr;
350
351	addr = (unsigned char *)sc->sc_base + reg;
352#if 0
353	printf("openpic: reading from %p (0x%08x + 0x%08x)\n", addr,
354	    sc->sc_base, reg);
355#endif
356
357	return in32rb(addr);
358}
359
360static void
361openpic_write(struct openpic_softc *sc, int reg, u_int val)
362{
363	volatile unsigned char *addr;
364
365	addr = (unsigned char *)sc->sc_base + reg;
366#if 0
367	printf("openpic: writing to %p (0x%08x + 0x%08x)\n", addr, sc->sc_base,
368	    reg);
369#endif
370
371	out32rb(addr, val);
372}
373
374static int
375openpic_read_irq(struct openpic_softc *sc, int cpu)
376{
377	return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
378}
379
380static void
381openpic_eoi(struct openpic_softc *sc, int cpu)
382{
383	openpic_write(sc, OPENPIC_EOI(cpu), 0);
384	openpic_read(sc, OPENPIC_EOI(cpu));
385}
386
387static void
388openpic_enable_irq(struct openpic_softc *sc, int irq, int type)
389{
390	u_int	x;
391
392	x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq));
393	x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE);
394	if (type == IST_LEVEL)
395		x |= OPENPIC_SENSE_LEVEL;
396	else
397		x |= OPENPIC_SENSE_EDGE;
398	openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
399}
400
401static void
402openpic_disable_irq(struct openpic_softc *sc, int irq)
403{
404	u_int	x;
405
406	x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq));
407	x |= OPENPIC_IMASK;
408	openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x);
409}
410
411static void
412openpic_set_priority(struct openpic_softc *sc, int cpu, int pri)
413{
414	u_int	x;
415
416	x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu));
417	x &= ~OPENPIC_CPU_PRIORITY_MASK;
418	x |= pri;
419	openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x);
420}
421
422static void
423openpic_intr(void)
424{
425	int		irq;
426	u_int32_t	msr;
427
428	msr = mfmsr();
429
430	irq = openpic_read_irq(softc, 0);
431	if (irq == 255) {
432		return;
433	}
434
435start:
436	openpic_disable_irq(softc, irq);
437	mtmsr(msr | PSL_EE);
438
439	/* do the interrupt thang */
440	intr_handle(irq);
441
442	mtmsr(msr);
443
444	openpic_eoi(softc, 0);
445
446	irq = openpic_read_irq(softc, 0);
447	if (irq != 255)
448		goto start;
449}
450
451static void
452irq_enable(int irq)
453{
454
455	openpic_enable_irq(softc, irq, IST_LEVEL);
456}
457
458static void
459irq_disable(int irq)
460{
461
462	openpic_disable_irq(softc, irq);
463}
464