1139825Simp/*-
286233Stmm * Copyright (c) 1999, 2000 Matthew R. Green
3200879Smarius * Copyright (c) 2009 by Marius Strobl <marius@FreeBSD.org>
486233Stmm * All rights reserved.
586233Stmm *
686233Stmm * Redistribution and use in source and binary forms, with or without
786233Stmm * modification, are permitted provided that the following conditions
886233Stmm * are met:
986233Stmm * 1. Redistributions of source code must retain the above copyright
1086233Stmm *    notice, this list of conditions and the following disclaimer.
1186233Stmm * 2. Redistributions in binary form must reproduce the above copyright
1286233Stmm *    notice, this list of conditions and the following disclaimer in the
1386233Stmm *    documentation and/or other materials provided with the distribution.
1486233Stmm * 3. The name of the author may not be used to endorse or promote products
1586233Stmm *    derived from this software without specific prior written permission.
1686233Stmm *
1786233Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1886233Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1986233Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2086233Stmm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2186233Stmm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2286233Stmm * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2386233Stmm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2486233Stmm * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2586233Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2686233Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2786233Stmm * SUCH DAMAGE.
2886233Stmm *
29219567Smarius *	from: NetBSD: ebus.c,v 1.52 2008/05/29 14:51:26 mrg Exp
3086233Stmm */
31219567Smarius/*-
32219567Smarius * Copyright (c) 2001 Thomas Moestl <tmm@FreeBSD.org>
33219567Smarius * All rights reserved.
34219567Smarius *
35219567Smarius * Redistribution and use in source and binary forms, with or without
36219567Smarius * modification, are permitted provided that the following conditions
37219567Smarius * are met:
38219567Smarius * 1. Redistributions of source code must retain the above copyright
39219567Smarius *    notice, this list of conditions and the following disclaimer.
40219567Smarius * 2. Redistributions in binary form must reproduce the above copyright
41219567Smarius *    notice, this list of conditions and the following disclaimer in the
42219567Smarius *    documentation and/or other materials provided with the distribution.
43219567Smarius * 3. The name of the author may not be used to endorse or promote products
44219567Smarius *    derived from this software without specific prior written permission.
45219567Smarius *
46219567Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
47219567Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48219567Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49219567Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
50219567Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
51219567Smarius * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
52219567Smarius * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
53219567Smarius * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
54219567Smarius * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55219567Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56219567Smarius * SUCH DAMAGE.
57219567Smarius */
5886233Stmm
59146409Smarius#include <sys/cdefs.h>
60146409Smarius__FBSDID("$FreeBSD$");
61146409Smarius
6286233Stmm/*
63200879Smarius * Driver for JBus to EBus and PCI to EBus bridges
6486233Stmm */
6586233Stmm
6686233Stmm#include <sys/param.h>
6786233Stmm#include <sys/systm.h>
6886233Stmm#include <sys/bus.h>
6986233Stmm#include <sys/kernel.h>
7086233Stmm#include <sys/malloc.h>
71130025Sphk#include <sys/module.h>
7286233Stmm
73128712Stmm#include <sys/rman.h>
74128712Stmm
75133589Smarius#include <dev/ofw/ofw_bus.h>
76152684Smarius#include <dev/ofw/ofw_bus_subr.h>
77119338Simp#include <dev/ofw/openfirm.h>
7893067Stmm
79200879Smarius#include <machine/bus.h>
80200879Smarius#ifndef SUN4V
81200879Smarius#include <machine/bus_common.h>
82200879Smarius#endif
83200879Smarius#include <machine/intr_machdep.h>
8486233Stmm#include <machine/resource.h>
8586233Stmm
86119291Simp#include <dev/pci/pcireg.h>
87119291Simp#include <dev/pci/pcivar.h>
8886233Stmm
8986233Stmm#include <sparc64/pci/ofw_pci.h>
9086233Stmm
9186233Stmm/*
92200879Smarius * The register, interrupt map and for the PCI variant also the ranges
93200879Smarius * properties are identical to the ISA ones.
9486233Stmm */
9586233Stmm#include <sparc64/isa/ofw_isa.h>
9686233Stmm
97200879Smariusstruct ebus_nexus_ranges {
98200879Smarius	uint32_t	child_hi;
99200879Smarius	uint32_t	child_lo;
100200879Smarius	uint32_t	phys_hi;
101200879Smarius	uint32_t	phys_lo;
102200879Smarius	uint32_t	size;
103200879Smarius};
104200879Smarius
10586233Stmmstruct ebus_devinfo {
106152684Smarius	struct ofw_bus_devinfo	edi_obdinfo;
10786233Stmm	struct resource_list	edi_rl;
10886233Stmm};
10986233Stmm
110128712Stmmstruct ebus_rinfo {
111128712Stmm	int			eri_rtype;
112128712Stmm	struct rman		eri_rman;
113128712Stmm	struct resource		*eri_res;
114128712Stmm};
115128712Stmm
11686233Stmmstruct ebus_softc {
117200879Smarius	void			*sc_range;
118128712Stmm	struct ebus_rinfo	*sc_rinfo;
11986233Stmm
120200879Smarius	u_int			sc_flags;
121200879Smarius#define	EBUS_PCI		(1 << 0)
122200879Smarius
12386233Stmm	int			sc_nrange;
124117119Stmm
125117119Stmm	struct ofw_bus_iinfo	sc_iinfo;
126200879Smarius
127200879Smarius#ifndef SUN4V
128200879Smarius	uint32_t		sc_ign;
129200879Smarius#endif
13086233Stmm};
13186233Stmm
132200879Smariusstatic device_probe_t ebus_nexus_probe;
133200879Smariusstatic device_attach_t ebus_nexus_attach;
134200879Smariusstatic device_probe_t ebus_pci_probe;
135200879Smariusstatic device_attach_t ebus_pci_attach;
136128712Stmmstatic bus_print_child_t ebus_print_child;
137128712Stmmstatic bus_probe_nomatch_t ebus_probe_nomatch;
138128712Stmmstatic bus_alloc_resource_t ebus_alloc_resource;
139225931Smariusstatic bus_activate_resource_t ebus_activate_resource;
140225931Smariusstatic bus_adjust_resource_t ebus_adjust_resource;
141128712Stmmstatic bus_release_resource_t ebus_release_resource;
142200879Smariusstatic bus_setup_intr_t ebus_setup_intr;
143128712Stmmstatic bus_get_resource_list_t ebus_get_resource_list;
144152684Smariusstatic ofw_bus_get_devinfo_t ebus_get_devinfo;
14586233Stmm
146200879Smariusstatic int ebus_attach(device_t dev, struct ebus_softc *sc, phandle_t node);
147200879Smariusstatic struct ebus_devinfo *ebus_setup_dinfo(device_t dev,
148200879Smarius    struct ebus_softc *sc, phandle_t node);
149200879Smariusstatic void ebus_destroy_dinfo(struct ebus_devinfo *edi);
150200879Smariusstatic int ebus_print_res(struct ebus_devinfo *edi);
15186233Stmm
152200879Smariusstatic devclass_t ebus_devclass;
153200879Smarius
154200879Smariusstatic device_method_t ebus_nexus_methods[] = {
15586233Stmm	/* Device interface */
156200879Smarius	DEVMETHOD(device_probe,		ebus_nexus_probe),
157200879Smarius	DEVMETHOD(device_attach,	ebus_nexus_attach),
158154870Smarius	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
159154870Smarius	DEVMETHOD(device_suspend,	bus_generic_suspend),
160154870Smarius	DEVMETHOD(device_resume,	bus_generic_resume),
16186233Stmm
16286233Stmm	/* Bus interface */
16386233Stmm	DEVMETHOD(bus_print_child,	ebus_print_child),
16486233Stmm	DEVMETHOD(bus_probe_nomatch,	ebus_probe_nomatch),
16586233Stmm	DEVMETHOD(bus_alloc_resource,	ebus_alloc_resource),
166225931Smarius	DEVMETHOD(bus_activate_resource, ebus_activate_resource),
16786233Stmm	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
168225931Smarius	DEVMETHOD(bus_adjust_resource,	ebus_adjust_resource),
169128712Stmm	DEVMETHOD(bus_release_resource,	ebus_release_resource),
170200879Smarius	DEVMETHOD(bus_setup_intr,	ebus_setup_intr),
171200879Smarius	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
172200879Smarius	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
173200879Smarius	DEVMETHOD(bus_get_resource_list, ebus_get_resource_list),
174200879Smarius	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
175200879Smarius
176200879Smarius	/* ofw_bus interface */
177200879Smarius	DEVMETHOD(ofw_bus_get_devinfo,	ebus_get_devinfo),
178200879Smarius	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
179200879Smarius	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
180200879Smarius	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
181200879Smarius	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
182200879Smarius	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
183200879Smarius
184227848Smarius	DEVMETHOD_END
185200879Smarius};
186200879Smarius
187200879Smariusstatic driver_t ebus_nexus_driver = {
188200879Smarius	"ebus",
189200879Smarius	ebus_nexus_methods,
190200879Smarius	sizeof(struct ebus_softc),
191200879Smarius};
192200879Smarius
193200879Smarius/*
194200879Smarius * NB: we rely on the interrupt controllers of the accompanying PCI-Express
195200879Smarius * bridge to be registered as the nexus variant of the EBus bridges doesn't
196200879Smarius * employ its own one.
197200879Smarius */
198200879SmariusEARLY_DRIVER_MODULE(ebus, nexus, ebus_nexus_driver, ebus_devclass, 0, 0,
199200879Smarius    BUS_PASS_BUS + 1);
200200879SmariusMODULE_DEPEND(ebus, nexus, 1, 1, 1);
201200879Smarius
202200879Smariusstatic device_method_t ebus_pci_methods[] = {
203200879Smarius	/* Device interface */
204200879Smarius	DEVMETHOD(device_probe,		ebus_pci_probe),
205200879Smarius	DEVMETHOD(device_attach,	ebus_pci_attach),
206200879Smarius	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
207200879Smarius	DEVMETHOD(device_suspend,	bus_generic_suspend),
208200879Smarius	DEVMETHOD(device_resume,	bus_generic_resume),
209200879Smarius
210200879Smarius	/* Bus interface */
211200879Smarius	DEVMETHOD(bus_print_child,	ebus_print_child),
212200879Smarius	DEVMETHOD(bus_probe_nomatch,	ebus_probe_nomatch),
213200879Smarius	DEVMETHOD(bus_alloc_resource,	ebus_alloc_resource),
214200879Smarius	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
215200879Smarius	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
216200879Smarius	DEVMETHOD(bus_release_resource,	ebus_release_resource),
217190099Smarius	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
218190099Smarius	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
219146967Smarius	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
220190099Smarius	DEVMETHOD(bus_get_resource_list, ebus_get_resource_list),
221186128Snwhitehorn	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
22286233Stmm
223133589Smarius	/* ofw_bus interface */
224152684Smarius	DEVMETHOD(ofw_bus_get_devinfo,	ebus_get_devinfo),
225152684Smarius	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
226152684Smarius	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
227152684Smarius	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
228152684Smarius	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
229152684Smarius	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
230133589Smarius
231227848Smarius	DEVMETHOD_END
23286233Stmm};
23386233Stmm
234200879Smariusstatic driver_t ebus_pci_driver = {
23586233Stmm	"ebus",
236200879Smarius	ebus_pci_methods,
23786233Stmm	sizeof(struct ebus_softc),
23886233Stmm};
23986233Stmm
240200879SmariusEARLY_DRIVER_MODULE(ebus, pci, ebus_pci_driver, ebus_devclass, 0, 0,
241200874Smarius    BUS_PASS_BUS);
242182066SmariusMODULE_DEPEND(ebus, pci, 1, 1, 1);
243182066SmariusMODULE_VERSION(ebus, 1);
24486233Stmm
24586233Stmmstatic int
246200879Smariusebus_nexus_probe(device_t dev)
24786233Stmm{
248200879Smarius	const char* compat;
24986233Stmm
250200879Smarius	compat = ofw_bus_get_compat(dev);
251200879Smarius	if (compat != NULL && strcmp(ofw_bus_get_name(dev), "ebus") == 0 &&
252200879Smarius	    strcmp(compat, "jbus-ebus") == 0) {
253200879Smarius		device_set_desc(dev, "JBus-EBus bridge");
254200879Smarius		return (BUS_PROBE_GENERIC);
255200879Smarius	}
256200879Smarius	return (ENXIO);
257200879Smarius}
258200879Smarius
259200879Smariusstatic int
260200879Smariusebus_pci_probe(device_t dev)
261200879Smarius{
262200879Smarius
26386233Stmm	if (pci_get_class(dev) != PCIC_BRIDGE ||
26486233Stmm	    pci_get_vendor(dev) != 0x108e ||
265146409Smarius	    strcmp(ofw_bus_get_name(dev), "ebus") != 0)
26686233Stmm		return (ENXIO);
26786233Stmm
26886233Stmm	if (pci_get_device(dev) == 0x1000)
26986233Stmm		device_set_desc(dev, "PCI-EBus2 bridge");
27086233Stmm	else if (pci_get_device(dev) == 0x1100)
27186233Stmm		device_set_desc(dev, "PCI-EBus3 bridge");
27286233Stmm	else
27386233Stmm		return (ENXIO);
274200879Smarius	return (BUS_PROBE_GENERIC);
275128712Stmm}
27686233Stmm
277128712Stmmstatic int
278200879Smariusebus_nexus_attach(device_t dev)
279128712Stmm{
280128712Stmm	struct ebus_softc *sc;
281200879Smarius	phandle_t node;
282200879Smarius
283200879Smarius	sc = device_get_softc(dev);
284200879Smarius	node = ofw_bus_get_node(dev);
285200879Smarius
286200879Smarius#ifndef SUN4V
287200879Smarius	if (OF_getprop(node, "portid", &sc->sc_ign,
288200879Smarius	    sizeof(sc->sc_ign)) == -1) {
289200879Smarius		device_printf(dev, "could not determine IGN");
290200879Smarius		return (ENXIO);
291200879Smarius	}
292200879Smarius#endif
293200879Smarius
294200879Smarius	sc->sc_nrange = OF_getprop_alloc(node, "ranges",
295200879Smarius	    sizeof(struct ebus_nexus_ranges), &sc->sc_range);
296200879Smarius	if (sc->sc_nrange == -1) {
297200879Smarius		printf("%s: could not get ranges property\n", __func__);
298200879Smarius		return (ENXIO);
299200879Smarius	}
300200879Smarius	return (ebus_attach(dev, sc, node));
301200879Smarius}
302200879Smarius
303200879Smariusstatic int
304200879Smariusebus_pci_attach(device_t dev)
305200879Smarius{
306200879Smarius	struct ebus_softc *sc;
307128712Stmm	struct ebus_rinfo *eri;
308128712Stmm	struct resource *res;
309128712Stmm	phandle_t node;
310128712Stmm	int i, rnum, rid;
31186233Stmm
31286233Stmm	sc = device_get_softc(dev);
313200879Smarius	sc->sc_flags |= EBUS_PCI;
31486233Stmm
315200879Smarius	pci_write_config(dev, PCIR_COMMAND,
316200879Smarius	    pci_read_config(dev, PCIR_COMMAND, 2) | PCIM_CMD_SERRESPEN |
317200879Smarius	    PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN, 2);
318200879Smarius	pci_write_config(dev, PCIR_CACHELNSZ, 16 /* 64 bytes */, 1);
319200879Smarius	pci_write_config(dev, PCIR_LATTIMER, 64 /* 64 PCI cycles */, 1);
320200879Smarius
321182066Smarius	node = ofw_bus_get_node(dev);
32286233Stmm	sc->sc_nrange = OF_getprop_alloc(node, "ranges",
323200879Smarius	    sizeof(struct isa_ranges), &sc->sc_range);
324128712Stmm	if (sc->sc_nrange == -1) {
325200879Smarius		printf("%s: could not get ranges property\n", __func__);
326128712Stmm		return (ENXIO);
327128712Stmm	}
32886233Stmm
329128712Stmm	sc->sc_rinfo = malloc(sizeof(*sc->sc_rinfo) * sc->sc_nrange, M_DEVBUF,
330128712Stmm	    M_WAITOK | M_ZERO);
331128712Stmm
332128712Stmm	/* For every range, there must be a matching resource. */
333128712Stmm	for (rnum = 0; rnum < sc->sc_nrange; rnum++) {
334128712Stmm		eri = &sc->sc_rinfo[rnum];
335200879Smarius		eri->eri_rtype = ofw_isa_range_restype(
336200879Smarius		    &((struct isa_ranges *)sc->sc_range)[rnum]);
337128712Stmm		rid = PCIR_BAR(rnum);
338128712Stmm		res = bus_alloc_resource_any(dev, eri->eri_rtype, &rid,
339128712Stmm		    RF_ACTIVE);
340128712Stmm		if (res == NULL) {
341200879Smarius			printf("%s: failed to allocate range resource!\n",
342200879Smarius			    __func__);
343128712Stmm			goto fail;
344128712Stmm		}
345128712Stmm		eri->eri_res = res;
346128712Stmm		eri->eri_rman.rm_type = RMAN_ARRAY;
347128712Stmm		eri->eri_rman.rm_descr = "EBus range";
348225931Smarius		if (rman_init_from_resource(&eri->eri_rman, res) != 0) {
349200879Smarius			printf("%s: failed to initialize rman!", __func__);
350128712Stmm			goto fail;
351128712Stmm		}
352128712Stmm	}
353200879Smarius	return (ebus_attach(dev, sc, node));
354128712Stmm
355200879Smarius fail:
356200879Smarius	for (i = rnum; i >= 0; i--) {
357200879Smarius		eri = &sc->sc_rinfo[i];
358200879Smarius		if (i < rnum)
359200879Smarius			rman_fini(&eri->eri_rman);
360200879Smarius		if (eri->eri_res != 0) {
361200879Smarius			bus_release_resource(dev, eri->eri_rtype,
362200879Smarius			    PCIR_BAR(rnum), eri->eri_res);
363200879Smarius		}
364200879Smarius	}
365200879Smarius	free(sc->sc_rinfo, M_DEVBUF);
366200879Smarius	free(sc->sc_range, M_OFWPROP);
367200879Smarius	return (ENXIO);
368200879Smarius}
369200879Smarius
370200879Smariusstatic int
371200879Smariusebus_attach(device_t dev, struct ebus_softc *sc, phandle_t node)
372200879Smarius{
373200879Smarius	struct ebus_devinfo *edi;
374200879Smarius	device_t cdev;
375200879Smarius
376117119Stmm	ofw_bus_setup_iinfo(node, &sc->sc_iinfo, sizeof(ofw_isa_intr_t));
377117119Stmm
37886233Stmm	/*
379117119Stmm	 * Now attach our children.
38086233Stmm	 */
38186233Stmm	for (node = OF_child(node); node > 0; node = OF_peer(node)) {
382152684Smarius		if ((edi = ebus_setup_dinfo(dev, sc, node)) == NULL)
38386233Stmm			continue;
384152684Smarius		if ((cdev = device_add_child(dev, NULL, -1)) == NULL) {
385152684Smarius			device_printf(dev, "<%s>: device_add_child failed\n",
386152684Smarius			    edi->edi_obdinfo.obd_name);
387152684Smarius			ebus_destroy_dinfo(edi);
38886233Stmm			continue;
38986233Stmm		}
39086233Stmm		device_set_ivars(cdev, edi);
39186233Stmm	}
392128712Stmm	return (bus_generic_attach(dev));
39386233Stmm}
39486233Stmm
39586233Stmmstatic int
39686233Stmmebus_print_child(device_t dev, device_t child)
39786233Stmm{
39886233Stmm	int retval;
39986233Stmm
40086233Stmm	retval = bus_print_child_header(dev, child);
401152684Smarius	retval += ebus_print_res(device_get_ivars(child));
40286233Stmm	retval += bus_print_child_footer(dev, child);
40386233Stmm	return (retval);
40486233Stmm}
40586233Stmm
40686233Stmmstatic void
40786233Stmmebus_probe_nomatch(device_t dev, device_t child)
40886233Stmm{
40986233Stmm
410152684Smarius	device_printf(dev, "<%s>", ofw_bus_get_name(child));
411152684Smarius	ebus_print_res(device_get_ivars(child));
41286233Stmm	printf(" (no driver attached)\n");
41386233Stmm}
41486233Stmm
41586233Stmmstatic struct resource *
41686233Stmmebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
41786233Stmm    u_long start, u_long end, u_long count, u_int flags)
41886233Stmm{
41986233Stmm	struct ebus_softc *sc;
42086233Stmm	struct resource_list *rl;
421128712Stmm	struct resource_list_entry *rle = NULL;
422128712Stmm	struct resource *res;
423225931Smarius	struct ebus_rinfo *eri;
424200879Smarius	struct ebus_nexus_ranges *enr;
425200879Smarius	uint64_t cend, cstart, offset;
426225931Smarius	int i, isdefault, passthrough, ridx;
42786233Stmm
428200879Smarius	isdefault = (start == 0UL && end == ~0UL);
429200879Smarius	passthrough = (device_get_parent(child) != bus);
430200879Smarius	sc = device_get_softc(bus);
431128712Stmm	rl = BUS_GET_RESOURCE_LIST(bus, child);
43286233Stmm	switch (type) {
433146409Smarius	case SYS_RES_MEMORY:
434128712Stmm		KASSERT(!(isdefault && passthrough),
435200879Smarius		    ("%s: passthrough of default allocation", __func__));
436128712Stmm		if (!passthrough) {
437146967Smarius			rle = resource_list_find(rl, type, *rid);
438128712Stmm			if (rle == NULL)
439128712Stmm				return (NULL);
440128712Stmm			KASSERT(rle->res == NULL,
441200879Smarius			    ("%s: resource entry is busy", __func__));
442128712Stmm			if (isdefault) {
443128712Stmm				start = rle->start;
444128712Stmm				count = ulmax(count, rle->count);
445128712Stmm				end = ulmax(rle->end, start + count - 1);
446128712Stmm			}
44786233Stmm		}
448128712Stmm
449200879Smarius		res = NULL;
450200879Smarius		if ((sc->sc_flags & EBUS_PCI) != 0) {
451200879Smarius			/*
452200879Smarius			 * Map EBus ranges to PCI ranges.  This may include
453200879Smarius			 * changing the allocation type.
454200879Smarius			 */
455200879Smarius			(void)ofw_isa_range_map(sc->sc_range, sc->sc_nrange,
456200879Smarius			    &start, &end, &ridx);
457225931Smarius			eri = &sc->sc_rinfo[ridx];
458225931Smarius			res = rman_reserve_resource(&eri->eri_rman, start,
459225931Smarius			    end, count, flags & ~RF_ACTIVE, child);
460200879Smarius			if (res == NULL)
461200879Smarius				return (NULL);
462200879Smarius			rman_set_rid(res, *rid);
463225931Smarius			if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(
464225931Smarius			    child, type, *rid, res) != 0) {
465200879Smarius				rman_release_resource(res);
466200879Smarius				return (NULL);
467200879Smarius			}
468200879Smarius		} else {
469200879Smarius			/* Map EBus ranges to nexus ranges. */
470200879Smarius			for (i = 0; i < sc->sc_nrange; i++) {
471200879Smarius				enr = &((struct ebus_nexus_ranges *)
472200879Smarius				    sc->sc_range)[i];
473200879Smarius				cstart = (((uint64_t)enr->child_hi) << 32) |
474200879Smarius				    enr->child_lo;
475200879Smarius				cend = cstart + enr->size - 1;
476200879Smarius				if (start >= cstart && end <= cend) {
477200879Smarius					offset =
478200879Smarius					    (((uint64_t)enr->phys_hi) << 32) |
479200879Smarius					    enr->phys_lo;
480200879Smarius					start += offset - cstart;
481200879Smarius					end += offset - cstart;
482200879Smarius					res = bus_generic_alloc_resource(bus,
483200879Smarius					    child, type, rid, start, end,
484200879Smarius					    count, flags);
485200879Smarius					break;
486200879Smarius				}
487200879Smarius			}
48886233Stmm		}
489128712Stmm		if (!passthrough)
490128712Stmm			rle->res = res;
491128712Stmm		return (res);
49286233Stmm	case SYS_RES_IRQ:
493128712Stmm		return (resource_list_alloc(rl, bus, child, type, rid, start,
494128712Stmm		    end, count, flags));
49586233Stmm	}
496139942Smarcel	return (NULL);
497128712Stmm}
49890610Stmm
499182066Smariusstatic int
500225931Smariusebus_activate_resource(device_t bus, device_t child, int type, int rid,
501225931Smarius    struct resource *res)
502225931Smarius{
503225931Smarius	struct ebus_softc *sc;
504225931Smarius	struct ebus_rinfo *eri;
505225931Smarius	bus_space_tag_t bt;
506225931Smarius	bus_space_handle_t bh;
507225931Smarius	int i, rv;
508225931Smarius
509225931Smarius	sc = device_get_softc(bus);
510225931Smarius	if ((sc->sc_flags & EBUS_PCI) != 0 && type == SYS_RES_MEMORY) {
511225931Smarius		for (i = 0; i < sc->sc_nrange; i++) {
512225931Smarius			eri = &sc->sc_rinfo[i];
513225931Smarius			if (rman_is_region_manager(res, &eri->eri_rman) != 0) {
514225931Smarius				bt = rman_get_bustag(eri->eri_res);
515225931Smarius				rv = bus_space_subregion(bt,
516225931Smarius				    rman_get_bushandle(eri->eri_res),
517225931Smarius				    rman_get_start(res) -
518225931Smarius				    rman_get_start(eri->eri_res),
519225931Smarius				    rman_get_size(res), &bh);
520225931Smarius				if (rv != 0)
521225931Smarius					return (rv);
522225931Smarius				rman_set_bustag(res, bt);
523225931Smarius				rman_set_bushandle(res, bh);
524225931Smarius				return (rman_activate_resource(res));
525225931Smarius			}
526225931Smarius		}
527225931Smarius		return (EINVAL);
528225931Smarius	}
529225931Smarius	return (bus_generic_activate_resource(bus, child, type, rid, res));
530225931Smarius}
531225931Smarius
532225931Smariusstatic int
533225931Smariusebus_adjust_resource(device_t bus __unused, device_t child __unused,
534225931Smarius    int type __unused, struct resource *res __unused, u_long start __unused,
535225931Smarius    u_long end __unused)
536225931Smarius{
537225931Smarius
538225931Smarius	return (ENXIO);
539225931Smarius}
540225931Smarius
541225931Smariusstatic int
542128712Stmmebus_release_resource(device_t bus, device_t child, int type, int rid,
543128712Stmm    struct resource *res)
544128712Stmm{
545200879Smarius	struct ebus_softc *sc;
546128712Stmm	struct resource_list *rl;
547128712Stmm	struct resource_list_entry *rle;
548200879Smarius	int passthrough, rv;
54986233Stmm
550200879Smarius	passthrough = (device_get_parent(child) != bus);
551128712Stmm	rl = BUS_GET_RESOURCE_LIST(bus, child);
552225931Smarius	sc = device_get_softc(bus);
553225931Smarius	if ((sc->sc_flags & EBUS_PCI) != 0 && type == SYS_RES_MEMORY) {
554225931Smarius		if ((rman_get_flags(res) & RF_ACTIVE) != 0 ){
555225931Smarius			rv = bus_deactivate_resource(child, type, rid, res);
556225931Smarius			if (rv != 0)
557225931Smarius				return (rv);
558225931Smarius		}
559225931Smarius		rv = rman_release_resource(res);
560225931Smarius		if (rv != 0)
561128712Stmm			return (rv);
562128712Stmm		if (!passthrough) {
563146967Smarius			rle = resource_list_find(rl, type, rid);
564200879Smarius			KASSERT(rle != NULL,
565200879Smarius			    ("%s: resource entry not found!", __func__));
566200879Smarius			KASSERT(rle->res != NULL,
567200879Smarius			   ("%s: resource entry is not busy", __func__));
568128712Stmm			rle->res = NULL;
56986233Stmm		}
570225931Smarius		return (0);
57186233Stmm	}
572225931Smarius	return (resource_list_release(rl, bus, child, type, rid, res));
57386233Stmm}
57486233Stmm
575200879Smariusstatic int
576200879Smariusebus_setup_intr(device_t dev, device_t child, struct resource *ires,
577200879Smarius    int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg,
578200879Smarius    void **cookiep)
579200879Smarius{
580200879Smarius#ifndef SUN4V
581200879Smarius	struct ebus_softc *sc;
582200879Smarius	u_long vec;
583200879Smarius
584200879Smarius	sc = device_get_softc(dev);
585200879Smarius	if ((sc->sc_flags & EBUS_PCI) == 0) {
586200879Smarius		/*
587200879Smarius		 * Make sure the vector is fully specified.  This isn't
588200879Smarius		 * necessarily the case with the PCI variant.
589200879Smarius		 */
590200879Smarius		vec = rman_get_start(ires);
591200879Smarius		if (INTIGN(vec) != sc->sc_ign) {
592200879Smarius			device_printf(dev,
593200879Smarius			    "invalid interrupt vector 0x%lx\n", vec);
594200879Smarius			return (EINVAL);
595200879Smarius		}
596200879Smarius
597200879Smarius		/*
598200879Smarius		 * As we rely on the interrupt controllers of the
599200879Smarius		 * accompanying PCI-Express bridge ensure at least
600200879Smarius		 * something is registered for this vector.
601200879Smarius		 */
602200879Smarius		if (intr_vectors[vec].iv_ic == NULL) {
603200879Smarius			device_printf(dev,
604200879Smarius			    "invalid interrupt controller for vector 0x%lx\n",
605200879Smarius			    vec);
606200879Smarius			return (EINVAL);
607200879Smarius		}
608200879Smarius	}
609200879Smarius#endif
610200879Smarius	return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr,
611200879Smarius	    arg, cookiep));
612200879Smarius}
613200879Smarius
61486233Stmmstatic struct resource_list *
61586233Stmmebus_get_resource_list(device_t dev, device_t child)
61686233Stmm{
61786233Stmm	struct ebus_devinfo *edi;
61886233Stmm
61986233Stmm	edi = device_get_ivars(child);
62086233Stmm	return (&edi->edi_rl);
62186233Stmm}
62286233Stmm
623152684Smariusstatic const struct ofw_bus_devinfo *
624152684Smariusebus_get_devinfo(device_t bus, device_t dev)
625152684Smarius{
626152684Smarius	struct ebus_devinfo *edi;
627152684Smarius
628152684Smarius	edi = device_get_ivars(dev);
629152684Smarius	return (&edi->edi_obdinfo);
630152684Smarius}
631152684Smarius
63286233Stmmstatic struct ebus_devinfo *
633152684Smariusebus_setup_dinfo(device_t dev, struct ebus_softc *sc, phandle_t node)
63486233Stmm{
635200879Smarius	struct isa_regs reg, *regs;
636200879Smarius	ofw_isa_intr_t intr, *intrs;
63786233Stmm	struct ebus_devinfo *edi;
638200879Smarius	uint64_t start;
639200879Smarius	uint32_t rintr;
640200879Smarius	int i, nintr, nreg, rv;
641200879Smarius	uint8_t maskbuf[sizeof(reg) + sizeof(intr)];
64286233Stmm
643111119Simp	edi = malloc(sizeof(*edi), M_DEVBUF, M_ZERO | M_WAITOK);
644152684Smarius	if (ofw_bus_gen_setup_devinfo(&edi->edi_obdinfo, node) != 0) {
645152684Smarius		free(edi, M_DEVBUF);
64686233Stmm		return (NULL);
647152684Smarius	}
64886233Stmm	resource_list_init(&edi->edi_rl);
649200879Smarius	nreg = OF_getprop_alloc(node, "reg", sizeof(*regs), (void **)&regs);
65086233Stmm	if (nreg == -1) {
651152684Smarius		device_printf(dev, "<%s>: incomplete\n",
652152684Smarius		    edi->edi_obdinfo.obd_name);
653200879Smarius		ebus_destroy_dinfo(edi);
654200879Smarius		return (NULL);
65586233Stmm	}
65686233Stmm	for (i = 0; i < nreg; i++) {
657200879Smarius		start = ISA_REG_PHYS(regs + i);
658200879Smarius		(void)resource_list_add(&edi->edi_rl, SYS_RES_MEMORY, i,
659200879Smarius		    start, start + regs[i].size - 1, regs[i].size);
66086233Stmm	}
661200879Smarius	free(regs, M_OFWPROP);
66286233Stmm
66386233Stmm	nintr = OF_getprop_alloc(node, "interrupts",  sizeof(*intrs),
66486233Stmm	    (void **)&intrs);
665200879Smarius	if (nintr == -1)
666200879Smarius		return (edi);
66786233Stmm	for (i = 0; i < nintr; i++) {
668200879Smarius		rv = 0;
669200879Smarius		if ((sc->sc_flags & EBUS_PCI) != 0) {
670200879Smarius			rintr = ofw_isa_route_intr(dev, node, &sc->sc_iinfo,
671200879Smarius			    intrs[i]);
672200879Smarius		} else {
673200879Smarius			intr = intrs[i];
674200879Smarius			rv = ofw_bus_lookup_imap(node, &sc->sc_iinfo, &reg,
675200879Smarius			    sizeof(reg), &intr, sizeof(intr), &rintr,
676209298Snwhitehorn			    sizeof(rintr), NULL, maskbuf);
677200879Smarius#ifndef SUN4V
678200879Smarius			if (rv != 0)
679200879Smarius				rintr = INTMAP_VEC(sc->sc_ign, rintr);
680200879Smarius#endif
681200879Smarius		}
682200879Smarius		if ((sc->sc_flags & EBUS_PCI) == 0 ? rv == 0 :
683200879Smarius		    rintr == PCI_INVALID_IRQ) {
684152684Smarius			device_printf(dev,
685152684Smarius			    "<%s>: could not map EBus interrupt %d\n",
686152684Smarius			    edi->edi_obdinfo.obd_name, intrs[i]);
687200879Smarius			continue;
688152684Smarius		}
689200879Smarius		(void)resource_list_add(&edi->edi_rl, SYS_RES_IRQ, i, rintr,
690200879Smarius		    rintr, 1);
69186233Stmm	}
692117119Stmm	free(intrs, M_OFWPROP);
69386233Stmm	return (edi);
69486233Stmm}
69586233Stmm
69686233Stmmstatic void
69786233Stmmebus_destroy_dinfo(struct ebus_devinfo *edi)
69886233Stmm{
69986233Stmm
70086233Stmm	resource_list_free(&edi->edi_rl);
701152684Smarius	ofw_bus_gen_destroy_devinfo(&edi->edi_obdinfo);
70286233Stmm	free(edi, M_DEVBUF);
70386233Stmm}
70486233Stmm
70586233Stmmstatic int
70686233Stmmebus_print_res(struct ebus_devinfo *edi)
70786233Stmm{
70886233Stmm	int retval;
70986233Stmm
71086233Stmm	retval = 0;
711146409Smarius	retval += resource_list_print_type(&edi->edi_rl, "addr", SYS_RES_MEMORY,
71286233Stmm	    "%#lx");
71386233Stmm	retval += resource_list_print_type(&edi->edi_rl, "irq", SYS_RES_IRQ,
71486233Stmm	    "%ld");
71586233Stmm	return (retval);
71686233Stmm}
717