ofwbus.c revision 99652
194755Sbenno/*
294755Sbenno * Copyright 1998 Massachusetts Institute of Technology
394755Sbenno *
494755Sbenno * Permission to use, copy, modify, and distribute this software and
594755Sbenno * its documentation for any purpose and without fee is hereby
694755Sbenno * granted, provided that both the above copyright notice and this
794755Sbenno * permission notice appear in all copies, that both the above
894755Sbenno * copyright notice and this permission notice appear in all
994755Sbenno * supporting documentation, and that the name of M.I.T. not be used
1094755Sbenno * in advertising or publicity pertaining to distribution of the
1194755Sbenno * software without specific, written prior permission.  M.I.T. makes
1294755Sbenno * no representations about the suitability of this software for any
1394755Sbenno * purpose.  It is provided "as is" without express or implied
1494755Sbenno * warranty.
1594755Sbenno *
1694755Sbenno * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1794755Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1894755Sbenno * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1994755Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2094755Sbenno * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2194755Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2294755Sbenno * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2394755Sbenno * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2494755Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2594755Sbenno * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2694755Sbenno * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2794755Sbenno * SUCH DAMAGE.
2894755Sbenno */
2994755Sbenno/*-
3094755Sbenno * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
3194755Sbenno *
3294755Sbenno * Redistribution and use in source and binary forms, with or without
3394755Sbenno * modification, are permitted provided that the following conditions
3494755Sbenno * are met:
3594755Sbenno * 1. Redistributions of source code must retain the above copyright
3694755Sbenno *    notice, this list of conditions and the following disclaimer.
3794755Sbenno * 2. Redistributions in binary form must reproduce the above copyright
3894755Sbenno *    notice, this list of conditions and the following disclaimer in the
3994755Sbenno *    documentation and/or other materials provided with the distribution.
4094755Sbenno *
4194755Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4294755Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4394755Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4494755Sbenno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4594755Sbenno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4694755Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4794755Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4894755Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4994755Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5094755Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5194755Sbenno * SUCH DAMAGE.
5294755Sbenno *
5394755Sbenno * 	from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
5494755Sbenno *
5594755Sbenno * $FreeBSD: head/sys/powerpc/aim/nexus.c 99652 2002-07-09 11:13:33Z benno $
5694755Sbenno */
5794755Sbenno
5894755Sbenno#include <sys/param.h>
5994755Sbenno#include <sys/systm.h>
6094755Sbenno#include <sys/bus.h>
6194755Sbenno#include <sys/cons.h>
6294755Sbenno#include <sys/kernel.h>
6394755Sbenno#include <sys/malloc.h>
6494755Sbenno
6594755Sbenno#include <dev/ofw/openfirm.h>
6694755Sbenno
6794755Sbenno#include <machine/bus.h>
6894755Sbenno#include <machine/frame.h>
6999652Sbenno#include <machine/intr_machdep.h>
7094755Sbenno#include <machine/nexusvar.h>
7194755Sbenno#include <machine/resource.h>
7294755Sbenno
7399652Sbenno#include <sys/rman.h>
7499652Sbenno
7599652Sbenno#include "pic_if.h"
7699652Sbenno
7794755Sbenno/*
7894755Sbenno * The nexus (which is a pseudo-bus actually) iterates over the nodes that
7994755Sbenno * exist in OpenFirmware and adds them as devices to this bus so that drivers
8094755Sbenno * can be attached to them.
8194755Sbenno *
8294755Sbenno * Maybe this code should get into dev/ofw to some extent, as some of it should
8394755Sbenno * work for all OpenFirmware based machines...
8494755Sbenno */
8594755Sbenno
8694755Sbennostatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
8794755Sbenno
8894755Sbennostruct nexus_devinfo {
8994755Sbenno	phandle_t	ndi_node;
9094755Sbenno	/* Some common properties. */
9194755Sbenno	char		*ndi_name;
9294755Sbenno	char		*ndi_device_type;
9399652Sbenno	char		*ndi_compatible;
9494755Sbenno};
9594755Sbenno
9694755Sbennostruct nexus_softc {
9799652Sbenno	device_t	sc_pic;
9894755Sbenno};
9994755Sbenno
10099652Sbenno/*
10199652Sbenno * Device interface
10299652Sbenno */
10399652Sbennostatic int	nexus_probe(device_t);
10499652Sbennostatic void	nexus_probe_nomatch(device_t, device_t);
10594755Sbenno
10699652Sbenno/*
10799652Sbenno * Bus interface
10899652Sbenno */
10999652Sbennostatic int	nexus_read_ivar(device_t, device_t, int, uintptr_t *);
11099652Sbennostatic int	nexus_write_ivar(device_t, device_t, int, uintptr_t);
11199652Sbennostatic int	nexus_setup_intr(device_t, device_t, struct resource *, int,
11299652Sbenno		    driver_intr_t *, void *, void **);
11399652Sbennostatic int	nexus_teardown_intr(device_t, device_t, struct resource *,
11499652Sbenno		    void *);
11599652Sbennostatic struct	resource *nexus_alloc_resource(device_t, device_t, int, int *,
11699652Sbenno		    u_long, u_long, u_long, u_int);
11799652Sbennostatic int	nexus_activate_resource(device_t, device_t, int, int,
11899652Sbenno		    struct resource *);
11999652Sbennostatic int	nexus_deactivate_resource(device_t, device_t, int, int,
12099652Sbenno		    struct resource *);
12199652Sbennostatic int	nexus_release_resource(device_t, device_t, int, int,
12299652Sbenno		    struct resource *);
12399652Sbenno
12499652Sbenno/*
12599652Sbenno * Local routines
12699652Sbenno */
12799652Sbennostatic device_t	create_device_from_node(device_t, phandle_t);
12899652Sbenno
12994755Sbennostatic device_method_t nexus_methods[] = {
13094755Sbenno	/* Device interface */
13194755Sbenno	DEVMETHOD(device_probe,		nexus_probe),
13294755Sbenno	DEVMETHOD(device_attach,	bus_generic_attach),
13394755Sbenno	DEVMETHOD(device_detach,	bus_generic_detach),
13494755Sbenno	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
13594755Sbenno	DEVMETHOD(device_suspend,	bus_generic_suspend),
13694755Sbenno	DEVMETHOD(device_resume,	bus_generic_resume),
13794755Sbenno
13894755Sbenno	/* Bus interface. Resource management is business of the children... */
13994755Sbenno	DEVMETHOD(bus_print_child,	bus_generic_print_child),
14094755Sbenno	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
14194755Sbenno	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
14294755Sbenno	DEVMETHOD(bus_write_ivar,	nexus_write_ivar),
14399652Sbenno	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
14499652Sbenno	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
14599652Sbenno	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
14699652Sbenno	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
14799652Sbenno	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
14899652Sbenno	DEVMETHOD(bus_release_resource,	nexus_release_resource),
14994755Sbenno
15094755Sbenno	{ 0, 0 }
15194755Sbenno};
15294755Sbenno
15394755Sbennostatic driver_t nexus_driver = {
15494755Sbenno	"nexus",
15594755Sbenno	nexus_methods,
15694755Sbenno	sizeof(struct nexus_softc),
15794755Sbenno};
15894755Sbenno
15994755Sbennostatic devclass_t nexus_devclass;
16094755Sbenno
16194755SbennoDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
16294755Sbenno
16394755Sbennostatic int
16494755Sbennonexus_probe(device_t dev)
16594755Sbenno{
16694755Sbenno	phandle_t	root;
16799652Sbenno	phandle_t	pic, cpus, child, new_node, temp_node;
16894755Sbenno	struct		nexus_softc *sc;
16994755Sbenno
17094755Sbenno	if ((root = OF_peer(0)) == -1)
17194755Sbenno		panic("nexus_probe: OF_peer failed.");
17294755Sbenno
17399652Sbenno	sc = device_get_softc(dev);
17499652Sbenno
17599652Sbenno	if ((cpus = OF_finddevice("/cpus")) != -1) {
17699652Sbenno		for (child = OF_child(cpus); child; child = OF_peer(child))
17799652Sbenno			(void)create_device_from_node(dev, child);
17899652Sbenno	}
17999652Sbenno
18099652Sbenno	if ((child = OF_finddevice("/chosen")) == -1)
18199652Sbenno		printf("nexus_probe: can't find /chosen");
18299652Sbenno
18399652Sbenno	if (OF_getprop(child, "interrupt-controller", &pic, 4) != 4)
18499652Sbenno		printf("nexus_probe: can't get interrupt-controller");
18599652Sbenno
18699652Sbenno	sc->sc_pic = create_device_from_node(dev, pic);
18799652Sbenno
18899652Sbenno	if (sc->sc_pic == NULL)
18999652Sbenno		printf("nexus_probe: failed to create PIC device");
19099652Sbenno
19194755Sbenno	child = root;
19294755Sbenno	while (child != 0) {
19399652Sbenno		if (child != pic)
19499652Sbenno			(void)create_device_from_node(dev, child);
19594755Sbenno
19699652Sbenno		if (child == cpus)
19799652Sbenno			new_node = 0;
19899652Sbenno		else
19999652Sbenno			new_node = OF_child(child);
20094755Sbenno		if (new_node == -1)
20199652Sbenno			panic("nexus_probe: OF_child returned -1");
20294755Sbenno		if (new_node == 0)
20394755Sbenno			new_node = OF_peer(child);
20494755Sbenno		if (new_node == 0) {
20594755Sbenno			temp_node = child;
20694755Sbenno			while (new_node == 0) {
20794755Sbenno				temp_node = OF_parent(temp_node);
20894755Sbenno				if (temp_node == 0)
20994755Sbenno					break;
21094755Sbenno				new_node = OF_peer(temp_node);
21194755Sbenno			}
21294755Sbenno		}
21394755Sbenno		child = new_node;
21494755Sbenno	}
21594755Sbenno	device_set_desc(dev, "OpenFirmware Nexus device");
21699652Sbenno
21799652Sbenno	{
21899652Sbenno		u_int	*foo = 0xf8000020;
21999652Sbenno		pmap_kenter(0xf8000000, 0xf8000000);
22099652Sbenno		printf(">>> uni_n_clock_ctl = %08x\n", *foo);
22199652Sbenno		pmap_kremove(0xf8000000);
22299652Sbenno	}
22394755Sbenno	return (0);
22494755Sbenno}
22594755Sbenno
22694755Sbennostatic void
22794755Sbennonexus_probe_nomatch(device_t dev, device_t child)
22894755Sbenno{
22994755Sbenno	char	*name, *type;
23094755Sbenno
23194755Sbenno	if (BUS_READ_IVAR(dev, child, NEXUS_IVAR_NAME,
23294755Sbenno	    (uintptr_t *)&name) != 0 ||
23394755Sbenno	    BUS_READ_IVAR(dev, child, NEXUS_IVAR_DEVICE_TYPE,
23494755Sbenno	    (uintptr_t *)&type) != 0)
23594755Sbenno		return;
23694755Sbenno
23794755Sbenno	if (type == NULL)
23894755Sbenno		type = "(unknown)";
23994755Sbenno#if 0
24094755Sbenno	device_printf(dev, "<%s>, type %s (no driver attached)\n",
24194755Sbenno	    name, type);
24294755Sbenno#endif
24394755Sbenno}
24494755Sbenno
24594755Sbennostatic int
24694755Sbennonexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
24794755Sbenno{
24894755Sbenno	struct nexus_devinfo *dinfo;
24994755Sbenno
25094755Sbenno	if ((dinfo = device_get_ivars(child)) == 0)
25194755Sbenno		return (ENOENT);
25294755Sbenno	switch (which) {
25394755Sbenno	case NEXUS_IVAR_NODE:
25494755Sbenno		*result = dinfo->ndi_node;
25594755Sbenno		break;
25694755Sbenno	case NEXUS_IVAR_NAME:
25794755Sbenno		*result = (uintptr_t)dinfo->ndi_name;
25894755Sbenno		break;
25994755Sbenno	case NEXUS_IVAR_DEVICE_TYPE:
26094755Sbenno		*result = (uintptr_t)dinfo->ndi_device_type;
26194755Sbenno		break;
26299652Sbenno	case NEXUS_IVAR_COMPATIBLE:
26399652Sbenno		*result = (uintptr_t)dinfo->ndi_compatible;
26499652Sbenno		break;
26594755Sbenno	default:
26694755Sbenno		return (ENOENT);
26794755Sbenno	}
26894755Sbenno	return 0;
26994755Sbenno}
27094755Sbenno
27194755Sbennostatic int
27294755Sbennonexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
27394755Sbenno{
27494755Sbenno	struct nexus_devinfo *dinfo;
27594755Sbenno
27694755Sbenno	if ((dinfo = device_get_ivars(child)) == 0)
27794755Sbenno		return (ENOENT);
27894755Sbenno
27994755Sbenno	switch (which) {
28094755Sbenno	case NEXUS_IVAR_NODE:
28194755Sbenno	case NEXUS_IVAR_NAME:
28294755Sbenno	case NEXUS_IVAR_DEVICE_TYPE:
28394755Sbenno		return (EINVAL);
28494755Sbenno	default:
28594755Sbenno		return (ENOENT);
28694755Sbenno	}
28794755Sbenno	return 0;
28894755Sbenno}
28999652Sbenno
29099652Sbennostatic int
29199652Sbennonexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
29299652Sbenno    driver_intr_t *intr, void *arg, void **cookiep)
29399652Sbenno{
29499652Sbenno	struct	nexus_softc *sc;
29599652Sbenno
29699652Sbenno	sc = device_get_softc(dev);
29799652Sbenno
29899652Sbenno	if (device_get_state(sc->sc_pic) != DS_ATTACHED)
29999652Sbenno		panic("nexus_setup_intr: no pic attached\n");
30099652Sbenno
30199652Sbenno	return (PIC_SETUP_INTR(sc->sc_pic, child, res, flags, intr, arg,
30299652Sbenno	    cookiep));
30399652Sbenno}
30499652Sbenno
30599652Sbennostatic int
30699652Sbennonexus_teardown_intr(device_t dev, device_t child, struct resource *res,
30799652Sbenno    void *ih)
30899652Sbenno{
30999652Sbenno	struct	nexus_softc *sc;
31099652Sbenno
31199652Sbenno	sc = device_get_softc(dev);
31299652Sbenno
31399652Sbenno	if (device_get_state(sc->sc_pic) != DS_ATTACHED)
31499652Sbenno		panic("nexus_teardown_intr: no pic attached\n");
31599652Sbenno
31699652Sbenno	return (PIC_TEARDOWN_INTR(sc->sc_pic, child, res, ih));
31799652Sbenno}
31899652Sbenno
31999652Sbenno/*
32099652Sbenno * Allocate resources at the behest of a child.  This only handles interrupts,
32199652Sbenno * since I/O resources are handled by child busses.
32299652Sbenno */
32399652Sbennostatic struct resource *
32499652Sbennonexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
32599652Sbenno    u_long start, u_long end, u_long count, u_int flags)
32699652Sbenno{
32799652Sbenno	struct	nexus_softc *sc;
32899652Sbenno	struct	resource *rv;
32999652Sbenno
33099652Sbenno	sc = device_get_softc(bus);
33199652Sbenno
33299652Sbenno	if (type != SYS_RES_IRQ) {
33399652Sbenno		device_printf(bus, "unknown resource request from %s\n",
33499652Sbenno		    device_get_nameunit(child));
33599652Sbenno		return (NULL);
33699652Sbenno	}
33799652Sbenno
33899652Sbenno	if (device_get_state(sc->sc_pic) != DS_ATTACHED)
33999652Sbenno		panic("nexus_alloc_resource: no pic attached\n");
34099652Sbenno
34199652Sbenno	rv = PIC_ALLOCATE_INTR(sc->sc_pic, child, rid, start, flags);
34299652Sbenno
34399652Sbenno	return (rv);
34499652Sbenno}
34599652Sbenno
34699652Sbennostatic int
34799652Sbennonexus_activate_resource(device_t bus, device_t child, int type, int rid,
34899652Sbenno    struct resource *res)
34999652Sbenno{
35099652Sbenno
35199652Sbenno	/* Not much to be done yet... */
35299652Sbenno	return (rman_activate_resource(res));
35399652Sbenno}
35499652Sbenno
35599652Sbennostatic int
35699652Sbennonexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
35799652Sbenno    struct resource *res)
35899652Sbenno{
35999652Sbenno
36099652Sbenno	/* Not much to be done yet... */
36199652Sbenno	return (rman_deactivate_resource(res));
36299652Sbenno}
36399652Sbenno
36499652Sbennostatic int
36599652Sbennonexus_release_resource(device_t bus, device_t child, int type, int rid,
36699652Sbenno    struct resource *res)
36799652Sbenno{
36899652Sbenno	struct	nexus_softc *sc;
36999652Sbenno
37099652Sbenno	sc = device_get_softc(bus);
37199652Sbenno
37299652Sbenno	if (type != SYS_RES_IRQ) {
37399652Sbenno		device_printf(bus, "unknown resource request from %s\n",
37499652Sbenno		    device_get_nameunit(child));
37599652Sbenno		return (NULL);
37699652Sbenno	}
37799652Sbenno
37899652Sbenno	if (device_get_state(sc->sc_pic) != DS_ATTACHED)
37999652Sbenno		panic("nexus_release_resource: no pic attached\n");
38099652Sbenno
38199652Sbenno	return (PIC_RELEASE_INTR(sc->sc_pic, child, rid, res));
38299652Sbenno}
38399652Sbenno
38499652Sbennostatic device_t
38599652Sbennocreate_device_from_node(device_t parent, phandle_t node)
38699652Sbenno{
38799652Sbenno	device_t	cdev;
38899652Sbenno	struct		nexus_devinfo *dinfo;
38999652Sbenno	char		*name, *type, *compatible;
39099652Sbenno
39199652Sbenno	OF_getprop_alloc(node, "name", 1, (void **)&name);
39299652Sbenno	OF_getprop_alloc(node, "device_type", 1, (void **)&type);
39399652Sbenno	OF_getprop_alloc(node, "compatible", 1, (void **)&compatible);
39499652Sbenno	cdev = device_add_child(parent, NULL, -1);
39599652Sbenno	if (cdev != NULL) {
39699652Sbenno		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
39799652Sbenno		dinfo->ndi_node = node;
39899652Sbenno		dinfo->ndi_name = name;
39999652Sbenno		dinfo->ndi_device_type = type;
40099652Sbenno		dinfo->ndi_compatible = compatible;
40199652Sbenno		device_set_ivars(cdev, dinfo);
40299652Sbenno	} else
40399652Sbenno		free(name, M_OFWPROP);
40499652Sbenno
40599652Sbenno	return (cdev);
40699652Sbenno}
407