fdtbus.c revision 228984
140024Sgibbs/*-
240024Sgibbs * Copyright (c) 2009-2010 The FreeBSD Foundation
340024Sgibbs * All rights reserved.
440024Sgibbs *
540024Sgibbs * This software was developed by Semihalf under sponsorship from
640024Sgibbs * the FreeBSD Foundation.
756979Sgibbs *
840024Sgibbs * Redistribution and use in source and binary forms, with or without
956979Sgibbs * modification, are permitted provided that the following conditions
1040024Sgibbs * are met:
1140024Sgibbs * 1. Redistributions of source code must retain the above copyright
1240024Sgibbs *    notice, this list of conditions and the following disclaimer.
1340024Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1440024Sgibbs *    notice, this list of conditions and the following disclaimer in the
1540024Sgibbs *    documentation and/or other materials provided with the distribution.
1640024Sgibbs *
1756979Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1840024Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1940024Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2040024Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2140024Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2240024Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2340024Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2440024Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2540024Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2640024Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2740024Sgibbs * SUCH DAMAGE.
2840024Sgibbs */
2940024Sgibbs
3040024Sgibbs#include <sys/cdefs.h>
3140024Sgibbs__FBSDID("$FreeBSD: head/sys/dev/fdt/fdtbus.c 228984 2011-12-30 04:00:31Z marcel $");
3240024Sgibbs
3350477Speter#include <sys/param.h>
3440024Sgibbs#include <sys/systm.h>
3540024Sgibbs#include <sys/ktr.h>
3640024Sgibbs#include <sys/kernel.h>
3740024Sgibbs#include <sys/module.h>
3840024Sgibbs#include <sys/bus.h>
3940024Sgibbs#include <sys/rman.h>
4040024Sgibbs#include <sys/malloc.h>
4140024Sgibbs
4240024Sgibbs#include <machine/fdt.h>
4340024Sgibbs
4440024Sgibbs#include <dev/ofw/openfirm.h>
4540024Sgibbs
4640024Sgibbs#include "fdt_common.h"
4740024Sgibbs#include "ofw_bus_if.h"
4840024Sgibbs
4940024Sgibbs#ifdef DEBUG
5040024Sgibbs#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
5140024Sgibbs    printf(fmt,##args); } while (0)
52117126Sscottl#else
53117126Sscottl#define debugf(fmt, args...)
5456979Sgibbs#endif
5540024Sgibbs
5640024Sgibbsstatic MALLOC_DEFINE(M_FDTBUS, "fdtbus", "FDTbus devices information");
5740024Sgibbs
5840024Sgibbsstruct fdtbus_devinfo {
5956979Sgibbs	phandle_t		di_node;
6040024Sgibbs	char			*di_name;
6156979Sgibbs	char			*di_type;
6256979Sgibbs	char			*di_compat;
6340024Sgibbs	struct resource_list	di_res;
6440024Sgibbs
6540024Sgibbs	/* Interrupts sense-level info for this device */
6640024Sgibbs	struct fdt_sense_level	di_intr_sl[DI_MAX_INTR_NUM];
6740024Sgibbs};
6840024Sgibbs
6940024Sgibbsstruct fdtbus_softc {
7040024Sgibbs	struct rman	sc_irq;
7140024Sgibbs	struct rman	sc_mem;
7240024Sgibbs};
7340024Sgibbs
7440024Sgibbs/*
7540024Sgibbs * Prototypes.
7640024Sgibbs */
7740024Sgibbsstatic void fdtbus_identify(driver_t *, device_t);
7840024Sgibbsstatic int fdtbus_probe(device_t);
7957679Sgibbsstatic int fdtbus_attach(device_t);
8040024Sgibbs
8140024Sgibbsstatic int fdtbus_print_child(device_t, device_t);
8240024Sgibbsstatic struct resource *fdtbus_alloc_resource(device_t, device_t, int,
8340024Sgibbs    int *, u_long, u_long, u_long, u_int);
8440024Sgibbsstatic int fdtbus_release_resource(device_t, device_t, int, int,
8540024Sgibbs    struct resource *);
8640024Sgibbsstatic int fdtbus_activate_resource(device_t, device_t, int, int,
8740024Sgibbs    struct resource *);
8840024Sgibbsstatic int fdtbus_deactivate_resource(device_t, device_t, int, int,
8940024Sgibbs    struct resource *);
9040024Sgibbsstatic int fdtbus_setup_intr(device_t, device_t, struct resource *, int,
9140024Sgibbs    driver_filter_t *, driver_intr_t *, void *, void **);
9240024Sgibbsstatic int fdtbus_teardown_intr(device_t, device_t, struct resource *,
9340024Sgibbs    void *);
9440024Sgibbs
9540024Sgibbsstatic const char *fdtbus_ofw_get_name(device_t, device_t);
9640024Sgibbsstatic phandle_t fdtbus_ofw_get_node(device_t, device_t);
9740024Sgibbsstatic const char *fdtbus_ofw_get_type(device_t, device_t);
9840024Sgibbsstatic const char *fdtbus_ofw_get_compat(device_t, device_t);
9940024Sgibbs
10040024Sgibbs/*
10140024Sgibbs * Local routines.
10240024Sgibbs */
10357679Sgibbsstatic void newbus_device_from_fdt_node(device_t, phandle_t);
10457679Sgibbs
10557679Sgibbs/*
10657679Sgibbs * Bus interface definition.
10757679Sgibbs */
10857679Sgibbsstatic device_method_t fdtbus_methods[] = {
10940024Sgibbs	/* Device interface */
11040024Sgibbs	DEVMETHOD(device_identify,	fdtbus_identify),
11140024Sgibbs	DEVMETHOD(device_probe,		fdtbus_probe),
11240024Sgibbs	DEVMETHOD(device_attach,	fdtbus_attach),
11340024Sgibbs	DEVMETHOD(device_detach,	bus_generic_detach),
11440024Sgibbs	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
11540024Sgibbs	DEVMETHOD(device_suspend,	bus_generic_suspend),
11640024Sgibbs	DEVMETHOD(device_resume,	bus_generic_resume),
11740024Sgibbs
11840024Sgibbs	/* Bus interface */
11940024Sgibbs	DEVMETHOD(bus_print_child,	fdtbus_print_child),
12040024Sgibbs	DEVMETHOD(bus_alloc_resource,	fdtbus_alloc_resource),
12140024Sgibbs	DEVMETHOD(bus_release_resource,	fdtbus_release_resource),
12240024Sgibbs	DEVMETHOD(bus_activate_resource, fdtbus_activate_resource),
12340024Sgibbs	DEVMETHOD(bus_deactivate_resource, fdtbus_deactivate_resource),
12440024Sgibbs	DEVMETHOD(bus_setup_intr,	fdtbus_setup_intr),
12540024Sgibbs	DEVMETHOD(bus_teardown_intr,	fdtbus_teardown_intr),
12640024Sgibbs
12740024Sgibbs	/* OFW bus interface */
12840024Sgibbs	DEVMETHOD(ofw_bus_get_node,	fdtbus_ofw_get_node),
12940024Sgibbs	DEVMETHOD(ofw_bus_get_name,	fdtbus_ofw_get_name),
13040024Sgibbs	DEVMETHOD(ofw_bus_get_type,	fdtbus_ofw_get_type),
13140024Sgibbs	DEVMETHOD(ofw_bus_get_compat,	fdtbus_ofw_get_compat),
13240024Sgibbs
13340024Sgibbs	{ 0, 0 }
13440024Sgibbs};
13540024Sgibbs
13640024Sgibbsstatic driver_t fdtbus_driver = {
13740024Sgibbs	"fdtbus",
13840024Sgibbs	fdtbus_methods,
13940024Sgibbs	sizeof(struct fdtbus_softc)
14040024Sgibbs};
14140024Sgibbs
14240024Sgibbsdevclass_t fdtbus_devclass;
14340024Sgibbs
14440024SgibbsDRIVER_MODULE(fdtbus, nexus, fdtbus_driver, fdtbus_devclass, 0, 0);
14540024Sgibbs
14640024Sgibbsstatic void
14740024Sgibbsfdtbus_identify(driver_t *driver, device_t parent)
14840024Sgibbs{
14940024Sgibbs
15040024Sgibbs	debugf("%s(driver=%p, parent=%p)\n", __func__, driver, parent);
15140024Sgibbs
15240024Sgibbs	if (device_find_child(parent, "fdtbus", -1) == NULL)
15340024Sgibbs		BUS_ADD_CHILD(parent, 0, "fdtbus", -1);
15440024Sgibbs}
15540024Sgibbs
15640024Sgibbsstatic int
15740024Sgibbsfdtbus_probe(device_t dev)
15840024Sgibbs{
15940024Sgibbs
16040024Sgibbs	debugf("%s(dev=%p); pass=%u\n", __func__, dev, bus_current_pass);
16140024Sgibbs
16240024Sgibbs	device_set_desc(dev, "FDT main bus");
16340024Sgibbs	if (!bootverbose)
16440024Sgibbs		device_quiet(dev);
16540024Sgibbs	return (BUS_PROBE_DEFAULT);
16640024Sgibbs}
16740024Sgibbs
16840024Sgibbsstatic int
16940024Sgibbsfdtbus_attach(device_t dev)
17040024Sgibbs{
17140024Sgibbs	phandle_t root;
17240024Sgibbs	phandle_t child;
17340024Sgibbs	struct fdtbus_softc *sc;
17440024Sgibbs	u_long start, end;
17540024Sgibbs	int error;
17640024Sgibbs
17740024Sgibbs	if ((root = OF_finddevice("/")) == -1)
17840024Sgibbs		panic("fdtbus_attach: no root node.");
17940024Sgibbs
18040024Sgibbs	sc = device_get_softc(dev);
18140024Sgibbs
18240024Sgibbs	/*
18340024Sgibbs	 * IRQ rman.
18440024Sgibbs	 */
18540024Sgibbs	start = 0;
18640024Sgibbs	end = FDT_INTR_MAX - 1;
18740024Sgibbs	sc->sc_irq.rm_start = start;
18840024Sgibbs	sc->sc_irq.rm_end = end;
18940024Sgibbs	sc->sc_irq.rm_type = RMAN_ARRAY;
19040024Sgibbs	sc->sc_irq.rm_descr = "Interrupt request lines";
19140024Sgibbs	if ((error = rman_init(&sc->sc_irq)) != 0) {
19240024Sgibbs		device_printf(dev, "could not init IRQ rman, error = %d\n",
19340024Sgibbs		    error);
19440024Sgibbs		return (error);
19540024Sgibbs	}
19640024Sgibbs	if ((error = rman_manage_region(&sc->sc_irq, start, end)) != 0) {
19740024Sgibbs		device_printf(dev, "could not manage IRQ region, error = %d\n",
19840024Sgibbs		    error);
19940024Sgibbs		return (error);
20040024Sgibbs	}
20140024Sgibbs
20240024Sgibbs	/*
20340024Sgibbs	 * Mem-mapped I/O space rman.
20440024Sgibbs	 */
20540024Sgibbs	start = 0;
20640024Sgibbs	end = ~0ul;
20740024Sgibbs	sc->sc_mem.rm_start = start;
20840024Sgibbs	sc->sc_mem.rm_end = end;
20940024Sgibbs	sc->sc_mem.rm_type = RMAN_ARRAY;
21040024Sgibbs	sc->sc_mem.rm_descr = "I/O memory";
21140024Sgibbs	if ((error = rman_init(&sc->sc_mem)) != 0) {
21240024Sgibbs		device_printf(dev, "could not init I/O mem rman, error = %d\n",
21340024Sgibbs		    error);
21440024Sgibbs		return (error);
21540024Sgibbs	}
21640024Sgibbs	if ((error = rman_manage_region(&sc->sc_mem, start, end)) != 0) {
21740024Sgibbs		device_printf(dev, "could not manage I/O mem region, "
21856979Sgibbs		    "error = %d\n", error);
21956979Sgibbs		return (error);
22056979Sgibbs	}
22156979Sgibbs
22240024Sgibbs	/*
22340024Sgibbs	 * Walk the FDT root node and add top-level devices as our children.
22440024Sgibbs	 */
22540024Sgibbs	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
22640024Sgibbs		/* Check and process 'status' property. */
22740024Sgibbs		if (!(fdt_is_enabled(child)))
22840024Sgibbs			continue;
22940024Sgibbs
23040024Sgibbs		newbus_device_from_fdt_node(dev, child);
23140024Sgibbs	}
23240024Sgibbs
23340024Sgibbs	return (bus_generic_attach(dev));
23440024Sgibbs}
23540024Sgibbs
23640024Sgibbsstatic int
23740024Sgibbsfdtbus_print_child(device_t dev, device_t child)
23840024Sgibbs{
23940024Sgibbs	struct fdtbus_devinfo *di;
24040420Sgibbs	struct resource_list *rl;
24140024Sgibbs	int rv;
24240024Sgibbs
24340024Sgibbs	di = device_get_ivars(child);
24440024Sgibbs	rl = &di->di_res;
24540024Sgibbs
24640024Sgibbs	rv = 0;
24740024Sgibbs	rv += bus_print_child_header(dev, child);
24840024Sgibbs	rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
24940024Sgibbs	rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
25040024Sgibbs	rv += bus_print_child_footer(dev, child);
25140024Sgibbs
25240024Sgibbs	return (rv);
25340024Sgibbs}
25440024Sgibbs
25540024Sgibbsstatic void
25640024Sgibbsnewbus_device_destroy(device_t dev)
25740024Sgibbs{
25840024Sgibbs	struct fdtbus_devinfo *di;
25940024Sgibbs
260115343Sscottl	di = device_get_ivars(dev);
26140024Sgibbs
26240024Sgibbs	free(di->di_name, M_OFWPROP);
26340024Sgibbs	free(di->di_type, M_OFWPROP);
26440024Sgibbs	free(di->di_compat, M_OFWPROP);
26540024Sgibbs
26640024Sgibbs	resource_list_free(&di->di_res);
26740024Sgibbs	free(di, M_FDTBUS);
26840024Sgibbs}
26940024Sgibbs
27040024Sgibbsstatic device_t
27140024Sgibbsnewbus_device_create(device_t dev_par, phandle_t node, char *name, char *type,
27240024Sgibbs    char *compat)
27340024Sgibbs{
27440024Sgibbs	device_t child;
27540024Sgibbs	struct fdtbus_devinfo *di;
27640024Sgibbs
27756979Sgibbs	child = device_add_child(dev_par, NULL, -1);
27840024Sgibbs	if (child == NULL) {
27940024Sgibbs		free(name, M_OFWPROP);
28056979Sgibbs		free(type, M_OFWPROP);
28156979Sgibbs		free(compat, M_OFWPROP);
28256979Sgibbs		return (NULL);
28356979Sgibbs	}
28440024Sgibbs
28540024Sgibbs	di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK);
28640024Sgibbs	di->di_node = node;
28740024Sgibbs	di->di_name = name;
28840024Sgibbs	di->di_type = type;
28956979Sgibbs	di->di_compat = compat;
29056979Sgibbs
29140024Sgibbs	resource_list_init(&di->di_res);
29240024Sgibbs
29340024Sgibbs	if (fdt_reg_to_rl(node, &di->di_res, fdt_immr_va)) {
29440024Sgibbs		device_printf(child, "could not process 'reg' property\n");
29540024Sgibbs		newbus_device_destroy(child);
29640024Sgibbs		child = NULL;
29740024Sgibbs		goto out;
29840024Sgibbs	}
29940024Sgibbs
30040024Sgibbs	if (fdt_intr_to_rl(node, &di->di_res, di->di_intr_sl)) {
30140024Sgibbs		device_printf(child, "could not process 'interrupts' "
30240024Sgibbs		    "property\n");
30340024Sgibbs		newbus_device_destroy(child);
30440024Sgibbs		child = NULL;
30540024Sgibbs		goto out;
30640024Sgibbs	}
30740024Sgibbs
30840024Sgibbs	device_set_ivars(child, di);
30940024Sgibbs	debugf("added child name='%s', node=%p\n", name, (void *)node);
31040024Sgibbs
31140024Sgibbsout:
31240024Sgibbs	return (child);
31340024Sgibbs}
31440024Sgibbs
31540024Sgibbsstatic device_t
31640024Sgibbsnewbus_pci_create(device_t dev_par, phandle_t dt_node, u_long par_base,
31740024Sgibbs    u_long par_size)
31840024Sgibbs{
31940024Sgibbs	pcell_t reg[3 + 2];
32040024Sgibbs	device_t dev_child;
32140024Sgibbs	u_long start, end, count;
32240024Sgibbs	struct fdtbus_devinfo *di;
32340024Sgibbs	char *name, *type, *compat;
32440024Sgibbs	int len;
32540024Sgibbs
32640024Sgibbs	OF_getprop_alloc(dt_node, "device_type", 1, (void **)&type);
32740024Sgibbs	if (!(type != NULL && strcmp(type, "pci") == 0)) {
32840024Sgibbs		/* Only process 'pci' subnodes. */
32940024Sgibbs		free(type, M_OFWPROP);
33040024Sgibbs		return (NULL);
33140024Sgibbs	}
33257679Sgibbs
33340024Sgibbs	OF_getprop_alloc(dt_node, "name", 1, (void **)&name);
33440024Sgibbs	OF_getprop_alloc(OF_parent(dt_node), "compatible", 1,
33540024Sgibbs	    (void **)&compat);
33640024Sgibbs
33740024Sgibbs	dev_child = device_add_child(dev_par, NULL, -1);
33840024Sgibbs	if (dev_child == NULL) {
33940024Sgibbs		free(name, M_OFWPROP);
34056979Sgibbs		free(type, M_OFWPROP);
34140024Sgibbs		free(compat, M_OFWPROP);
34240024Sgibbs		return (NULL);
34340024Sgibbs	}
34440024Sgibbs
34540024Sgibbs	di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK);
34640024Sgibbs	di->di_node = dt_node;
34740024Sgibbs	di->di_name = name;
34840024Sgibbs	di->di_type = type;
34940024Sgibbs	di->di_compat = compat;
35040024Sgibbs
35140024Sgibbs	resource_list_init(&di->di_res);
35240024Sgibbs
35340024Sgibbs	/*
35440024Sgibbs	 * Produce and set SYS_RES_MEMORY resources.
35540024Sgibbs	 */
35640024Sgibbs	start = 0;
35740024Sgibbs	count = 0;
35840024Sgibbs
35940024Sgibbs	len = OF_getprop(dt_node, "reg", &reg, sizeof(reg));
36040024Sgibbs	if (len > 0) {
36140024Sgibbs		if (fdt_data_verify((void *)&reg[1], 2) != 0) {
36240024Sgibbs			device_printf(dev_child, "'reg' address value out of "
36340024Sgibbs			    "range\n");
36456979Sgibbs			newbus_device_destroy(dev_child);
36540024Sgibbs			dev_child = NULL;
36640024Sgibbs			goto out;
36740024Sgibbs		}
36840024Sgibbs		start = fdt_data_get((void *)&reg[1], 2);
36940024Sgibbs
37040024Sgibbs		if (fdt_data_verify((void *)&reg[3], 2) != 0) {
37140024Sgibbs			device_printf(dev_child, "'reg' size value out of "
37240024Sgibbs			    "range\n");
37340024Sgibbs			newbus_device_destroy(dev_child);
37440024Sgibbs			dev_child = NULL;
37540024Sgibbs			goto out;
37640024Sgibbs		}
37740024Sgibbs		count = fdt_data_get((void *)&reg[3], 2);
37840024Sgibbs	}
37940024Sgibbs
38040024Sgibbs	/* Calculate address range relative to base. */
38140024Sgibbs	par_base &= 0x000ffffful;
38240024Sgibbs	start &= 0x000ffffful;
38340024Sgibbs	start += par_base + fdt_immr_va;
38456979Sgibbs	if (count == 0)
38540024Sgibbs		count = par_size;
38640024Sgibbs	end = start + count - 1;
38740024Sgibbs
38840024Sgibbs	debugf("start = 0x%08lx, end = 0x%08lx, count = 0x%08lx\n",
38940024Sgibbs	    start, end, count);
39056979Sgibbs
39140024Sgibbs	if (count > par_size) {
39240024Sgibbs		device_printf(dev_child, "'reg' size value out of range\n");
39340024Sgibbs		newbus_device_destroy(dev_child);
39456979Sgibbs		dev_child = NULL;
39540024Sgibbs		goto out;
39640024Sgibbs	}
39740024Sgibbs
39856979Sgibbs	resource_list_add(&di->di_res, SYS_RES_MEMORY, 0, start, end, count);
39956979Sgibbs
40056979Sgibbs	/*
40157679Sgibbs	 * Set SYS_RES_IRQ resources.
40256979Sgibbs	 */
40356979Sgibbs	if (fdt_intr_to_rl(OF_parent(dt_node), &di->di_res, di->di_intr_sl)) {
40456979Sgibbs		device_printf(dev_child, "could not process 'interrupts' "
40556979Sgibbs		    "property\n");
40656979Sgibbs		newbus_device_destroy(dev_child);
40756979Sgibbs		dev_child = NULL;
40856979Sgibbs		goto out;
40957679Sgibbs	}
41057679Sgibbs
41157679Sgibbs	device_set_ivars(dev_child, di);
41256979Sgibbs	debugf("added child name='%s', node=%p\n", name,
41356979Sgibbs	    (void *)dt_node);
41456979Sgibbs
41540024Sgibbsout:
41656979Sgibbs	return (dev_child);
41756979Sgibbs}
41840024Sgibbs
41940024Sgibbsstatic void
42040024Sgibbspci_from_fdt_node(device_t dev_par, phandle_t dt_node, char *name,
42140024Sgibbs    char *type, char *compat)
42256979Sgibbs{
42340024Sgibbs	u_long reg_base, reg_size;
42440024Sgibbs	phandle_t dt_child;
42540024Sgibbs
42640024Sgibbs	/*
42740024Sgibbs	 * Retrieve 'reg' property.
42840024Sgibbs	 */
42940024Sgibbs	if (fdt_regsize(dt_node, &reg_base, &reg_size) != 0) {
43040024Sgibbs		device_printf(dev_par, "could not retrieve 'reg' prop\n");
43140024Sgibbs		return;
43240024Sgibbs	}
43340024Sgibbs
43440024Sgibbs	/*
43540024Sgibbs	 * Walk the PCI node and instantiate newbus devices representing
43640024Sgibbs	 * logical resources (bridges / ports).
43740024Sgibbs	 */
43840024Sgibbs	for (dt_child = OF_child(dt_node); dt_child != 0;
43940024Sgibbs	    dt_child = OF_peer(dt_child)) {
44040024Sgibbs
44140024Sgibbs		if (!(fdt_is_enabled(dt_child)))
44240024Sgibbs			continue;
44340024Sgibbs
44440024Sgibbs		newbus_pci_create(dev_par, dt_child, reg_base, reg_size);
44540024Sgibbs	}
44640024Sgibbs}
44740024Sgibbs
44840024Sgibbs/*
44940024Sgibbs * These FDT nodes do not need a corresponding newbus device object.
45040024Sgibbs */
45140024Sgibbsstatic char *fdt_devices_skip[] = {
45240024Sgibbs	"aliases",
45340024Sgibbs	"chosen",
45440024Sgibbs	"memory",
45540024Sgibbs	NULL
45640024Sgibbs};
45740024Sgibbs
45840024Sgibbsstatic void
45940024Sgibbsnewbus_device_from_fdt_node(device_t dev_par, phandle_t node)
46040024Sgibbs{
46140024Sgibbs	char *name, *type, *compat;
46240024Sgibbs	device_t child;
46340024Sgibbs	int i;
46440024Sgibbs
46540024Sgibbs	OF_getprop_alloc(node, "name", 1, (void **)&name);
46640024Sgibbs	OF_getprop_alloc(node, "device_type", 1, (void **)&type);
46740024Sgibbs	OF_getprop_alloc(node, "compatible", 1, (void **)&compat);
46840024Sgibbs
46940024Sgibbs	for (i = 0; fdt_devices_skip[i] != NULL; i++)
47040024Sgibbs		if (name != NULL && strcmp(name, fdt_devices_skip[i]) == 0) {
47140024Sgibbs			debugf("skipping instantiating FDT device='%s'\n",
47240024Sgibbs			    name);
47340024Sgibbs			return;
47440024Sgibbs		}
47540024Sgibbs
47640024Sgibbs	child = newbus_device_create(dev_par, node, name, type, compat);
47740024Sgibbs	if (type != NULL && strcmp(type, "pci") == 0)
47840024Sgibbs		pci_from_fdt_node(child, node, name, type, compat);
47940024Sgibbs}
48040024Sgibbs
48140024Sgibbsstatic struct resource *
48240024Sgibbsfdtbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
48340024Sgibbs    u_long start, u_long end, u_long count, u_int flags)
48440024Sgibbs{
48540024Sgibbs	struct fdtbus_softc *sc;
48640024Sgibbs	struct resource *res;
48740024Sgibbs	struct rman *rm;
48840024Sgibbs	struct fdtbus_devinfo *di;
48940024Sgibbs	struct resource_list_entry *rle;
49040024Sgibbs	int needactivate;
49140024Sgibbs
49240024Sgibbs	/*
49340024Sgibbs	 * Request for the default allocation with a given rid: use resource
49440024Sgibbs	 * list stored in the local device info.
49540024Sgibbs	 */
49640024Sgibbs	if ((start == 0UL) && (end == ~0UL)) {
49740024Sgibbs		if ((di = device_get_ivars(child)) == NULL)
49840024Sgibbs			return (NULL);
49940024Sgibbs
50040024Sgibbs		if (type == SYS_RES_IOPORT)
50140024Sgibbs			type = SYS_RES_MEMORY;
50240024Sgibbs
50340024Sgibbs		rle = resource_list_find(&di->di_res, type, *rid);
50440024Sgibbs		if (rle == NULL) {
50540024Sgibbs			device_printf(bus, "no default resources for "
50640024Sgibbs			    "rid = %d, type = %d\n", *rid, type);
50740024Sgibbs			return (NULL);
50840024Sgibbs		}
50957679Sgibbs		start = rle->start;
51057679Sgibbs		end = rle->end;
51140024Sgibbs		count = rle->count;
51240024Sgibbs	}
51340024Sgibbs
51440024Sgibbs	sc = device_get_softc(bus);
51540024Sgibbs
51640024Sgibbs	needactivate = flags & RF_ACTIVE;
51740024Sgibbs	flags &= ~RF_ACTIVE;
51840024Sgibbs
51940024Sgibbs	switch (type) {
52040024Sgibbs	case SYS_RES_IRQ:
52140024Sgibbs		rm = &sc->sc_irq;
52240024Sgibbs		break;
52340024Sgibbs
52440024Sgibbs	case SYS_RES_IOPORT:
52540024Sgibbs	case SYS_RES_MEMORY:
52640024Sgibbs		rm = &sc->sc_mem;
52740024Sgibbs		break;
52840024Sgibbs
52940024Sgibbs	default:
53040024Sgibbs		return (NULL);
53140024Sgibbs	}
53240024Sgibbs
53340024Sgibbs	res = rman_reserve_resource(rm, start, end, count, flags, child);
53440024Sgibbs	if (res == NULL) {
53540024Sgibbs		device_printf(bus, "failed to reserve resource %#lx - %#lx "
53640024Sgibbs		    "(%#lx)\n", start, end, count);
53740024Sgibbs		return (NULL);
53856979Sgibbs	}
53956979Sgibbs
54056979Sgibbs	rman_set_rid(res, *rid);
54140024Sgibbs
54240024Sgibbs	if (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY) {
54340024Sgibbs		/* XXX endianess should be set based on SOC node */
54440024Sgibbs		rman_set_bustag(res, fdtbus_bs_tag);
54540024Sgibbs		rman_set_bushandle(res, rman_get_start(res));
54640024Sgibbs	}
54740024Sgibbs
54840024Sgibbs	if (needactivate)
54940024Sgibbs		if (bus_activate_resource(child, type, *rid, res)) {
55040024Sgibbs			device_printf(child, "resource activation failed\n");
55140024Sgibbs			rman_release_resource(res);
55240024Sgibbs			return (NULL);
55340024Sgibbs		}
55440024Sgibbs
55540024Sgibbs	return (res);
55640024Sgibbs}
55740024Sgibbs
55840024Sgibbsstatic int
55940024Sgibbsfdtbus_release_resource(device_t bus, device_t child, int type, int rid,
56040024Sgibbs    struct resource *res)
56140024Sgibbs{
56240024Sgibbs	int err;
56340024Sgibbs
56440024Sgibbs	if (rman_get_flags(res) & RF_ACTIVE) {
56540024Sgibbs		err = bus_deactivate_resource(child, type, rid, res);
56640024Sgibbs		if (err)
56740024Sgibbs			return (err);
56840024Sgibbs	}
56940024Sgibbs
57040024Sgibbs	return (rman_release_resource(res));
57140024Sgibbs}
57240024Sgibbs
57340024Sgibbsstatic int
57440024Sgibbsfdtbus_setup_intr(device_t bus, device_t child, struct resource *res,
57540024Sgibbs    int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg,
57640024Sgibbs    void **cookiep)
57740024Sgibbs{
57840024Sgibbs	int err;
57940024Sgibbs
58040024Sgibbs	*cookiep = 0;
58140024Sgibbs	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
58240024Sgibbs		flags |= INTR_EXCL;
58340024Sgibbs
58440024Sgibbs	err = rman_activate_resource(res);
58540024Sgibbs	if (err)
58640024Sgibbs		return (err);
58740024Sgibbs
58840024Sgibbs#if defined(__powerpc__)
58940024Sgibbs	err = powerpc_setup_intr(device_get_nameunit(child),
59040024Sgibbs	    rman_get_start(res), filter, ihand, arg, flags, cookiep);
59140024Sgibbs#elif defined(__mips__)
59256979Sgibbs	cpu_establish_hardintr(device_get_nameunit(child),
59356979Sgibbs		filter, ihand, arg, rman_get_start(res), flags, cookiep);
59456979Sgibbs#elif defined(__arm__)
59556979Sgibbs	arm_setup_irqhandler(device_get_nameunit(child),
59656979Sgibbs	    filter, ihand, arg, rman_get_start(res), flags, cookiep);
59740024Sgibbs	arm_unmask_irq(rman_get_start(res));
59840024Sgibbs	err = 0;
59940024Sgibbs#endif
60046581Sken
60146581Sken	return (err);
60256979Sgibbs}
60356979Sgibbs
60456979Sgibbsstatic int
60556979Sgibbsfdtbus_activate_resource(device_t bus, device_t child, int type, int rid,
60640024Sgibbs    struct resource *res)
60756979Sgibbs{
60856979Sgibbs
60956979Sgibbs	return (rman_activate_resource(res));
61056979Sgibbs}
61156979Sgibbs
61256979Sgibbsstatic int
61340024Sgibbsfdtbus_deactivate_resource(device_t bus, device_t child, int type, int rid,
61446581Sken    struct resource *res)
61546581Sken{
61646581Sken
61756979Sgibbs	return (rman_deactivate_resource(res));
61856979Sgibbs}
61956979Sgibbs
62040024Sgibbsstatic int
62140024Sgibbsfdtbus_teardown_intr(device_t bus, device_t child, struct resource *res,
62240024Sgibbs    void *cookie)
62340024Sgibbs{
62440024Sgibbs
62556979Sgibbs#if defined(__powerpc__)
62640024Sgibbs	return (powerpc_teardown_intr(cookie));
62740024Sgibbs#elif defined(__mips__)
62856979Sgibbs	/* mips does not have a teardown yet */
62956979Sgibbs	return (0);
63056979Sgibbs#elif defined(__arm__)
63156979Sgibbs	return (arm_remove_irqhandler(rman_get_start(res), cookie));
63256979Sgibbs#endif
63356979Sgibbs}
63456979Sgibbs
63556979Sgibbsstatic const char *
63656979Sgibbsfdtbus_ofw_get_name(device_t bus, device_t dev)
63756979Sgibbs{
63840024Sgibbs	struct fdtbus_devinfo *di;
63956979Sgibbs
64040024Sgibbs	return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_name);
64140024Sgibbs}
64256979Sgibbs
64340024Sgibbsstatic phandle_t
64440024Sgibbsfdtbus_ofw_get_node(device_t bus, device_t dev)
64540024Sgibbs{
64640024Sgibbs	struct fdtbus_devinfo *di;
64740024Sgibbs
64840024Sgibbs	return ((di = device_get_ivars(dev)) == NULL ? 0 : di->di_node);
64940024Sgibbs}
65040024Sgibbs
65140024Sgibbsstatic const char *
65240024Sgibbsfdtbus_ofw_get_type(device_t bus, device_t dev)
65340024Sgibbs{
65440024Sgibbs	struct fdtbus_devinfo *di;
65540024Sgibbs
65640024Sgibbs	return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_type);
65740024Sgibbs}
65840024Sgibbs
65940024Sgibbsstatic const char *
66056979Sgibbsfdtbus_ofw_get_compat(device_t bus, device_t dev)
66156979Sgibbs{
66240024Sgibbs	struct fdtbus_devinfo *di;
66340024Sgibbs
66440024Sgibbs	return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_compat);
66540024Sgibbs}
66640024Sgibbs
66740024Sgibbs
66840024Sgibbs