upa.c revision 225931
1101099Srwatson/*-
2101099Srwatson * Copyright (c) 2006 Marius Strobl <marius@FreeBSD.org>
3101099Srwatson * All rights reserved.
4101099Srwatson *
5101099Srwatson * Redistribution and use in source and binary forms, with or without
6101099Srwatson * modification, are permitted provided that the following conditions
7101099Srwatson * are met:
8101099Srwatson * 1. Redistributions of source code must retain the above copyright
9101099Srwatson *    notice, this list of conditions and the following disclaimer.
10101099Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11101099Srwatson *    notice, this list of conditions and the following disclaimer in the
12101099Srwatson *    documentation and/or other materials provided with the distribution.
13101099Srwatson *
14101099Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15101099Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16101099Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17101099Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18101099Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19101099Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20101099Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21101099Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22101099Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23101099Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24101099Srwatson * SUCH DAMAGE.
25101099Srwatson */
26101099Srwatson
27101099Srwatson#include <sys/cdefs.h>
28101099Srwatson__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/upa.c 225931 2011-10-02 23:22:38Z marius $");
29101099Srwatson
30101099Srwatson#include <sys/param.h>
31101099Srwatson#include <sys/systm.h>
32101099Srwatson#include <sys/bus.h>
33101099Srwatson#include <sys/kernel.h>
34101099Srwatson#include <sys/module.h>
35101099Srwatson#include <sys/resource.h>
36101099Srwatson#include <sys/rman.h>
37101099Srwatson
38101099Srwatson#include <dev/ofw/ofw_bus.h>
39101099Srwatson#include <dev/ofw/ofw_bus_subr.h>
40101099Srwatson#include <dev/ofw/openfirm.h>
41101099Srwatson
42101099Srwatson#include <machine/bus.h>
43101099Srwatson#include <machine/bus_common.h>
44101099Srwatson#include <machine/intr_machdep.h>
45101099Srwatson#include <machine/resource.h>
46101099Srwatson
47101099Srwatson#define	UPA_NREG	3
48101099Srwatson
49101099Srwatson#define	UPA_CFG		0
50101099Srwatson#define	UPA_IMR1	1
51101099Srwatson#define	UPA_IMR2	2
52101099Srwatson
53101099Srwatson/* UPA_CFG bank */
54101099Srwatson#define	UPA_CFG_UPA0			0x00	/* UPA0 config register */
55101099Srwatson#define	UPA_CFG_UPA1			0x08	/* UPA1 config register */
56101099Srwatson#define	UPA_CFG_IF			0x10	/* interface config register */
57101099Srwatson#define	 UPA_CFG_IF_RST			0x00
58101099Srwatson#define	 UPA_CFG_IF_POK_RST		0x02
59101099Srwatson#define	 UPA_CFG_IF_POK			0x03
60101099Srwatson#define	UPA_CFG_ESTAR			0x18	/* Estar config register */
61101099Srwatson#define	 UPA_CFG_ESTAR_SPEED_FULL	0x01
62101099Srwatson#define	 UPA_CFG_ESTAR_SPEED_1_2	0x02
63101099Srwatson#define	 UPA_CFG_ESTAR_SPEED_1_64	0x40
64101099Srwatson
65101099Srwatson#define	UPA_INO_BASE			0x2a
66101099Srwatson#define	UPA_INO_MAX			0x2b
67101099Srwatson
68101099Srwatsonstruct upa_regs {
69101099Srwatson	uint64_t	phys;
70101099Srwatson	uint64_t	size;
71101099Srwatson};
72101099Srwatson
73101099Srwatsonstruct upa_ranges {
74101099Srwatson	uint64_t	child;
75101099Srwatson	uint64_t	parent;
76101099Srwatson	uint64_t	size;
77101099Srwatson};
78101099Srwatson
79101099Srwatsonstruct upa_devinfo {
80101099Srwatson	struct ofw_bus_devinfo	udi_obdinfo;
81101099Srwatson	struct resource_list	udi_rl;
82101099Srwatson};
83101099Srwatson
84101099Srwatsonstruct upa_softc {
85101099Srwatson	struct resource		*sc_res[UPA_NREG];
86101099Srwatson	bus_space_tag_t		sc_bt[UPA_NREG];
87101099Srwatson	bus_space_handle_t	sc_bh[UPA_NREG];
88101099Srwatson
89101099Srwatson	uint32_t		sc_ign;
90101099Srwatson
91101099Srwatson	int			sc_nrange;
92101099Srwatson	struct upa_ranges	*sc_ranges;
93101099Srwatson};
94101099Srwatson
95101099Srwatson#define	UPA_READ(sc, reg, off) \
96101099Srwatson	bus_space_read_8((sc)->sc_bt[(reg)], (sc)->sc_bh[(reg)], (off))
97101099Srwatson#define	UPA_WRITE(sc, reg, off, val) \
98101099Srwatson	bus_space_write_8((sc)->sc_bt[(reg)], (sc)->sc_bh[(reg)], (off), (val))
99101099Srwatson
100101099Srwatsonstatic device_probe_t upa_probe;
101101099Srwatsonstatic device_attach_t upa_attach;
102101099Srwatsonstatic bus_print_child_t upa_print_child;
103101099Srwatsonstatic bus_probe_nomatch_t upa_probe_nomatch;
104101099Srwatsonstatic bus_alloc_resource_t upa_alloc_resource;
105101099Srwatsonstatic bus_adjust_resource_t upa_adjust_resource;
106101099Srwatsonstatic bus_setup_intr_t upa_setup_intr;
107101099Srwatsonstatic bus_get_resource_list_t upa_get_resource_list;
108101099Srwatsonstatic ofw_bus_get_devinfo_t upa_get_devinfo;
109101099Srwatson
110101099Srwatsonstatic void upa_intr_enable(void *);
111101099Srwatsonstatic void upa_intr_disable(void *);
112101099Srwatsonstatic void upa_intr_assign(void *);
113101099Srwatsonstatic struct upa_devinfo *upa_setup_dinfo(device_t, struct upa_softc *,
114101099Srwatson    phandle_t, uint32_t);
115101099Srwatsonstatic void upa_destroy_dinfo(struct upa_devinfo *);
116101099Srwatsonstatic int upa_print_res(struct upa_devinfo *);
117101099Srwatson
118101099Srwatsonstatic device_method_t upa_methods[] = {
119101099Srwatson	/* Device interface */
120101099Srwatson	DEVMETHOD(device_probe,		upa_probe),
121101099Srwatson	DEVMETHOD(device_attach,	upa_attach),
122101099Srwatson	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
123101099Srwatson	DEVMETHOD(device_suspend,	bus_generic_suspend),
124101099Srwatson	DEVMETHOD(device_resume,	bus_generic_resume),
125101099Srwatson
126101099Srwatson	/* Bus interface */
127101099Srwatson	DEVMETHOD(bus_print_child,	upa_print_child),
128101099Srwatson	DEVMETHOD(bus_probe_nomatch,	upa_probe_nomatch),
129101099Srwatson	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
130101099Srwatson	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
131101099Srwatson	DEVMETHOD(bus_alloc_resource,	upa_alloc_resource),
132101099Srwatson	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
133101099Srwatson	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
134101099Srwatson	DEVMETHOD(bus_adjust_resource,	upa_adjust_resource),
135101099Srwatson	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
136101099Srwatson	DEVMETHOD(bus_setup_intr,	upa_setup_intr),
137101099Srwatson	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
138101099Srwatson	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
139101099Srwatson	DEVMETHOD(bus_get_resource_list, upa_get_resource_list),
140101099Srwatson	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
141101099Srwatson
142101099Srwatson	/* ofw_bus interface */
143101099Srwatson	DEVMETHOD(ofw_bus_get_devinfo,	upa_get_devinfo),
144101099Srwatson	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
145101099Srwatson	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
146101099Srwatson	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
147101099Srwatson	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
148101099Srwatson	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
149101099Srwatson
150101099Srwatson	KOBJMETHOD_END
151101099Srwatson};
152101099Srwatson
153101099Srwatsonstatic devclass_t upa_devclass;
154101099Srwatson
155101099SrwatsonDEFINE_CLASS_0(upa, upa_driver, upa_methods, sizeof(struct upa_softc));
156101099SrwatsonEARLY_DRIVER_MODULE(upa, nexus, upa_driver, upa_devclass, 0, 0, BUS_PASS_BUS);
157101099Srwatson
158101099Srwatsonstatic const struct intr_controller upa_ic = {
159101099Srwatson	upa_intr_enable,
160101099Srwatson	upa_intr_disable,
161101099Srwatson	upa_intr_assign,
162101099Srwatson	/* The interrupts are pulse type and thus automatically cleared. */
163101099Srwatson	NULL
164101099Srwatson};
165101099Srwatson
166101099Srwatsonstruct upa_icarg {
167101099Srwatson	struct upa_softc	*uica_sc;
168101099Srwatson	u_int			uica_imr;
169101099Srwatson};
170101099Srwatson
171101099Srwatsonstatic int
172101099Srwatsonupa_probe(device_t dev)
173101099Srwatson{
174101099Srwatson	const char* compat;
175101099Srwatson
176101099Srwatson	compat = ofw_bus_get_compat(dev);
177101099Srwatson	if (compat != NULL && strcmp(ofw_bus_get_name(dev), "upa") == 0 &&
178101099Srwatson	    strcmp(compat, "upa64s") == 0) {
179101099Srwatson		device_set_desc(dev, "UPA bridge");
180101099Srwatson		return (BUS_PROBE_DEFAULT);
181101099Srwatson	}
182101099Srwatson	return (ENXIO);
183101099Srwatson}
184101099Srwatson
185101099Srwatsonstatic int
186101099Srwatsonupa_attach(device_t dev)
187101099Srwatson{
188101099Srwatson	struct upa_devinfo *udi;
189101099Srwatson	struct upa_icarg *uica;
190101099Srwatson	struct upa_softc *sc;
191101099Srwatson	phandle_t child, node;
192101099Srwatson	device_t cdev;
193101099Srwatson	uint32_t portid;
194101099Srwatson	int i, imr, j, rid;
195101099Srwatson#if 1
196101099Srwatson	device_t *children, schizo;
197101099Srwatson	u_long scount, sstart, ucount, ustart;
198101099Srwatson	int nchildren;
199101099Srwatson#endif
200101099Srwatson
201101099Srwatson	sc = device_get_softc(dev);
202101099Srwatson	node = ofw_bus_get_node(dev);
203101099Srwatson	for (i = UPA_CFG; i <= UPA_IMR2; i++) {
204101099Srwatson		rid = i;
205101099Srwatson		/*
206101099Srwatson		 * The UPA_IMR{1,2} resources are shared with that of the
207101099Srwatson		 * Schizo PCI bus B CSR bank.
208101099Srwatson		 */
209101099Srwatson#if 0
210101099Srwatson		sc->sc_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
211101099Srwatson		    &rid, ((i == UPA_IMR1 || i == UPA_IMR2) ? RF_SHAREABLE :
212101099Srwatson		    0) | RF_ACTIVE);
213101099Srwatson		if (sc->sc_res[i] == NULL) {
214101099Srwatson			device_printf(dev,
215101099Srwatson			    "could not allocate resource %d\n", i);
216101099Srwatson			goto fail;
217101099Srwatson		}
218101099Srwatson		sc->sc_bt[i] = rman_get_bustag(sc->sc_res[i]);
219101099Srwatson		sc->sc_bh[i] = rman_get_bushandle(sc->sc_res[i]);
220101099Srwatson#else
221101099Srwatson		/*
222101099Srwatson		 * Workaround for the fact that rman(9) only allows to
223101099Srwatson		 * share resources of the same size.
224101099Srwatson		 */
225101099Srwatson		if (i == UPA_IMR1 || i == UPA_IMR2) {
226101099Srwatson			if (bus_get_resource(dev, SYS_RES_MEMORY, i, &ustart,
227101099Srwatson			    &ucount) != 0) {
228101099Srwatson				device_printf(dev,
229101099Srwatson				    "could not determine UPA resource\n");
230101099Srwatson				goto fail;
231101099Srwatson			}
232101099Srwatson			if (device_get_children(device_get_parent(dev),
233101099Srwatson			    &children, &nchildren) != 0) {
234101099Srwatson				device_printf(dev, "could not get children\n");
235101099Srwatson				goto fail;
236101099Srwatson			}
237101099Srwatson			schizo = NULL;
238101099Srwatson			for (j = 0; j < nchildren; j++) {
239101099Srwatson				if (ofw_bus_get_type(children[j]) != NULL &&
240101099Srwatson				    strcmp(ofw_bus_get_type(children[j]),
241101099Srwatson				    "pci") == 0 &&
242101099Srwatson				    ofw_bus_get_compat(children[j]) != NULL &&
243101099Srwatson				    strcmp(ofw_bus_get_compat(children[j]),
244101099Srwatson				    "pci108e,8001") == 0 &&
245101099Srwatson				    ((bus_get_resource_start(children[j],
246101099Srwatson				    SYS_RES_MEMORY, 0) >> 20) & 1) == 1) {
247101099Srwatson					schizo = children[j];
248101099Srwatson					break;
249101099Srwatson				}
250101099Srwatson			}
251101099Srwatson			free(children, M_TEMP);
252101099Srwatson			if (schizo == NULL) {
253101099Srwatson				device_printf(dev, "could not find Schizo\n");
254101099Srwatson				goto fail;
255101099Srwatson			}
256101099Srwatson			if (bus_get_resource(schizo, SYS_RES_MEMORY, 0,
257101099Srwatson			    &sstart, &scount) != 0) {
258101099Srwatson				device_printf(dev,
259101099Srwatson				    "could not determine Schizo resource\n");
260101099Srwatson				goto fail;
261101099Srwatson			}
262101099Srwatson			sc->sc_res[i] = bus_alloc_resource(dev, SYS_RES_MEMORY,
263101099Srwatson			    &rid, sstart, sstart + scount - 1, scount,
264101099Srwatson			    RF_SHAREABLE | RF_ACTIVE);
265101099Srwatson		} else
266101099Srwatson			sc->sc_res[i] = bus_alloc_resource_any(dev,
267101099Srwatson			    SYS_RES_MEMORY, &rid, RF_ACTIVE);
268101099Srwatson		if (sc->sc_res[i] == NULL) {
269101099Srwatson			device_printf(dev,
270101099Srwatson			    "could not allocate resource %d\n", i);
271101099Srwatson			goto fail;
272101099Srwatson		}
273101099Srwatson		sc->sc_bt[i] = rman_get_bustag(sc->sc_res[i]);
274101099Srwatson		sc->sc_bh[i] = rman_get_bushandle(sc->sc_res[i]);
275101099Srwatson		if (i == UPA_IMR1 || i == UPA_IMR2)
276101099Srwatson			bus_space_subregion(sc->sc_bt[i], sc->sc_bh[i],
277101099Srwatson			    ustart - sstart, ucount, &sc->sc_bh[i]);
278101099Srwatson#endif
279101099Srwatson	}
280101099Srwatson
281101099Srwatson	if (OF_getprop(node, "portid", &sc->sc_ign, sizeof(sc->sc_ign)) == -1) {
282101099Srwatson		device_printf(dev, "could not determine IGN\n");
283101099Srwatson		goto fail;
284101099Srwatson	}
285101099Srwatson
286101099Srwatson	sc->sc_nrange = OF_getprop_alloc(node, "ranges", sizeof(*sc->sc_ranges),
287101099Srwatson	    (void **)&sc->sc_ranges);
288101099Srwatson	if (sc->sc_nrange == -1) {
289101099Srwatson		device_printf(dev, "could not determine ranges\n");
290101099Srwatson		goto fail;
291101099Srwatson	}
292101099Srwatson
293101099Srwatson	/*
294101099Srwatson	 * Hunt through all the interrupt mapping regs and register our
295101099Srwatson	 * interrupt controller for the corresponding interrupt vectors.
296101099Srwatson	 * We do this early in order to be able to catch stray interrupts.
297101099Srwatson	 */
298101099Srwatson	for (i = UPA_INO_BASE; i <= UPA_INO_MAX; i++) {
299101099Srwatson		imr = 0;
300101099Srwatson		for (j = UPA_IMR1; j <= UPA_IMR2; j++) {
301101099Srwatson			if (INTVEC(UPA_READ(sc, j, 0x0)) ==
302101099Srwatson			    INTMAP_VEC(sc->sc_ign, i)) {
303101099Srwatson				imr = j;
304101099Srwatson				break;
305101099Srwatson			}
306101099Srwatson		}
307101099Srwatson		if (imr == 0)
308101099Srwatson			continue;
309101099Srwatson		uica = malloc(sizeof(*uica), M_DEVBUF, M_NOWAIT);
310101099Srwatson		if (uica == NULL)
311101099Srwatson			panic("%s: could not allocate interrupt controller "
312101099Srwatson			    "argument", __func__);
313101099Srwatson		uica->uica_sc = sc;
314101099Srwatson		uica->uica_imr = imr;
315101099Srwatson#ifdef UPA_DEBUG
316101099Srwatson		device_printf(dev, "intr map (INO %d) IMR%d: %#lx\n",
317101099Srwatson		    i, imr, (u_long)UPA_READ(sc, imr, 0x0));
318101099Srwatson#endif
319101099Srwatson		j = intr_controller_register(INTMAP_VEC(sc->sc_ign, i),
320101099Srwatson		    &upa_ic, uica);
321101099Srwatson		if (j != 0)
322101099Srwatson			device_printf(dev, "could not register interrupt "
323101099Srwatson			    "controller for INO %d (%d)\n", i, j);
324101099Srwatson	}
325101099Srwatson
326101099Srwatson	/* Make sure the power level is appropriate for normal operation. */
327101099Srwatson	if (UPA_READ(sc, UPA_CFG, UPA_CFG_IF) != UPA_CFG_IF_POK) {
328101099Srwatson		if (bootverbose)
329101099Srwatson			device_printf(dev, "applying power\n");
330101099Srwatson		UPA_WRITE(sc, UPA_CFG, UPA_CFG_ESTAR, UPA_CFG_ESTAR_SPEED_1_2);
331101099Srwatson		UPA_WRITE(sc, UPA_CFG, UPA_CFG_ESTAR, UPA_CFG_ESTAR_SPEED_FULL);
332101099Srwatson		(void)UPA_READ(sc, UPA_CFG, UPA_CFG_ESTAR);
333101099Srwatson		UPA_WRITE(sc, UPA_CFG, UPA_CFG_IF, UPA_CFG_IF_POK_RST);
334101099Srwatson		(void)UPA_READ(sc, UPA_CFG, UPA_CFG_IF);
335101099Srwatson		DELAY(20000);
336101099Srwatson		UPA_WRITE(sc, UPA_CFG, UPA_CFG_IF, UPA_CFG_IF_POK);
337101099Srwatson		(void)UPA_READ(sc, UPA_CFG, UPA_CFG_IF);
338101099Srwatson	}
339101099Srwatson
340101099Srwatson	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
341101099Srwatson		/*
342101099Srwatson		 * The `upa-portid' properties of the children are used as
343101099Srwatson		 * index for the interrupt mapping registers.
344101099Srwatson		 * The `upa-portid' properties are also used to make up the
345101099Srwatson		 * INOs of the children as the values contained in their
346101099Srwatson		 * `interrupts' properties are bogus.
347101099Srwatson		 */
348101099Srwatson		if (OF_getprop(child, "upa-portid", &portid,
349101099Srwatson		    sizeof(portid)) == -1) {
350101099Srwatson			device_printf(dev,
351101099Srwatson			    "could not determine upa-portid of child 0x%lx\n",
352101099Srwatson			    (unsigned long)child);
353101099Srwatson			continue;
354101099Srwatson		}
355101099Srwatson		if (portid > 1) {
356101099Srwatson			device_printf(dev,
357101099Srwatson			    "upa-portid %d of child 0x%lx invalid\n", portid,
358101099Srwatson			    (unsigned long)child);
359101099Srwatson			continue;
360101099Srwatson		}
361101099Srwatson		if ((udi = upa_setup_dinfo(dev, sc, child, portid)) == NULL)
362101099Srwatson			continue;
363101099Srwatson		if ((cdev = device_add_child(dev, NULL, -1)) == NULL) {
364101099Srwatson			device_printf(dev, "<%s>: device_add_child failed\n",
365101099Srwatson			    udi->udi_obdinfo.obd_name);
366101099Srwatson			upa_destroy_dinfo(udi);
367101099Srwatson			continue;
368101099Srwatson		}
369101099Srwatson		device_set_ivars(cdev, udi);
370101099Srwatson	}
371101099Srwatson
372101099Srwatson	return (bus_generic_attach(dev));
373101099Srwatson
374101099Srwatson fail:
375101099Srwatson	for (i = UPA_CFG; i <= UPA_IMR2 && sc->sc_res[i] != NULL; i++)
376101099Srwatson		bus_release_resource(dev, SYS_RES_MEMORY,
377101099Srwatson		    rman_get_rid(sc->sc_res[i]), sc->sc_res[i]);
378101099Srwatson	return (ENXIO);
379101099Srwatson}
380101099Srwatson
381101099Srwatsonstatic int
382101099Srwatsonupa_print_child(device_t dev, device_t child)
383101099Srwatson{
384101099Srwatson	int rv;
385101099Srwatson
386101099Srwatson	rv = bus_print_child_header(dev, child);
387101099Srwatson	rv += upa_print_res(device_get_ivars(child));
388101099Srwatson	rv += bus_print_child_footer(dev, child);
389101099Srwatson	return (rv);
390101099Srwatson}
391101099Srwatson
392101099Srwatsonstatic void
393101099Srwatsonupa_probe_nomatch(device_t dev, device_t child)
394101099Srwatson{
395101099Srwatson	const char *type;
396101099Srwatson
397101099Srwatson	device_printf(dev, "<%s>", ofw_bus_get_name(child));
398101099Srwatson	upa_print_res(device_get_ivars(child));
399101099Srwatson	type = ofw_bus_get_type(child);
400101099Srwatson	printf(" type %s (no driver attached)\n",
401101099Srwatson	    type != NULL ? type : "unknown");
402101099Srwatson}
403101099Srwatson
404101099Srwatsonstatic struct resource *
405101099Srwatsonupa_alloc_resource(device_t dev, device_t child, int type, int *rid,
406101099Srwatson    u_long start, u_long end, u_long count, u_int flags)
407101099Srwatson{
408101099Srwatson	struct resource_list *rl;
409101099Srwatson	struct resource_list_entry *rle;
410101099Srwatson	struct upa_softc *sc;
411101099Srwatson	struct resource *rv;
412101099Srwatson	bus_addr_t cend, cstart;
413101099Srwatson	int i, isdefault, passthrough;
414101099Srwatson
415101099Srwatson	isdefault = (start == 0UL && end == ~0UL);
416101099Srwatson	passthrough = (device_get_parent(child) != dev);
417101099Srwatson	sc = device_get_softc(dev);
418101099Srwatson	rl = BUS_GET_RESOURCE_LIST(dev, child);
419101099Srwatson	rle = NULL;
420101099Srwatson	switch (type) {
421101099Srwatson	case SYS_RES_IRQ:
422101099Srwatson		return (resource_list_alloc(rl, dev, child, type, rid, start,
423101099Srwatson		    end, count, flags));
424101099Srwatson	case SYS_RES_MEMORY:
425101099Srwatson		if (!passthrough) {
426101099Srwatson			rle = resource_list_find(rl, type, *rid);
427101099Srwatson			if (rle == NULL)
428101099Srwatson				return (NULL);
429101099Srwatson			if (rle->res != NULL)
430101099Srwatson				panic("%s: resource entry is busy", __func__);
431101099Srwatson			if (isdefault) {
432101099Srwatson				start = rle->start;
433101099Srwatson				count = ulmax(count, rle->count);
434101099Srwatson				end = ulmax(rle->end, start + count - 1);
435101099Srwatson			}
436101099Srwatson		}
437101099Srwatson		for (i = 0; i < sc->sc_nrange; i++) {
438101099Srwatson			cstart = sc->sc_ranges[i].child;
439101099Srwatson			cend = cstart + sc->sc_ranges[i].size - 1;
440101099Srwatson			if (start < cstart || start > cend)
441101099Srwatson				continue;
442101099Srwatson			if (end < cstart || end > cend)
443101099Srwatson				return (NULL);
444101099Srwatson			start += sc->sc_ranges[i].parent - cstart;
445101099Srwatson			end += sc->sc_ranges[i].parent - cstart;
446101099Srwatson			rv = bus_generic_alloc_resource(dev, child, type, rid,
447101099Srwatson			    start, end, count, flags);
448101099Srwatson			if (!passthrough)
449101099Srwatson				rle->res = rv;
450101099Srwatson			return (rv);
451101099Srwatson		}
452101099Srwatson		/* FALLTHROUGH */
453101099Srwatson	default:
454101099Srwatson		return (NULL);
455101099Srwatson	}
456101099Srwatson}
457101099Srwatson
458101099Srwatsonstatic void
459101099Srwatsonupa_intr_enable(void *arg)
460101099Srwatson{
461101099Srwatson	struct intr_vector *iv = arg;
462101099Srwatson	struct upa_icarg *uica = iv->iv_icarg;
463101099Srwatson
464101099Srwatson	UPA_WRITE(uica->uica_sc, uica->uica_imr, 0x0,
465101099Srwatson	    INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
466101099Srwatson	(void)UPA_READ(uica->uica_sc, uica->uica_imr, 0x0);
467101099Srwatson}
468101099Srwatson
469101099Srwatsonstatic void
470101099Srwatsonupa_intr_disable(void *arg)
471101099Srwatson{
472101099Srwatson	struct intr_vector *iv = arg;
473101099Srwatson	struct upa_icarg *uica = iv->iv_icarg;
474101099Srwatson
475101099Srwatson	UPA_WRITE(uica->uica_sc, uica->uica_imr, 0x0, iv->iv_vec);
476101099Srwatson	(void)UPA_READ(uica->uica_sc, uica->uica_imr, 0x0);
477101099Srwatson}
478101099Srwatson
479101099Srwatsonstatic void
480101099Srwatsonupa_intr_assign(void *arg)
481101099Srwatson{
482101099Srwatson	struct intr_vector *iv = arg;
483101099Srwatson	struct upa_icarg *uica = iv->iv_icarg;
484101099Srwatson
485101099Srwatson	UPA_WRITE(uica->uica_sc, uica->uica_imr, 0x0, INTMAP_TID(
486101099Srwatson	    UPA_READ(uica->uica_sc, uica->uica_imr, 0x0), iv->iv_mid));
487101099Srwatson	(void)UPA_READ(uica->uica_sc, uica->uica_imr, 0x0);
488101099Srwatson}
489101099Srwatson
490101099Srwatsonstatic int
491101099Srwatsonupa_setup_intr(device_t dev, device_t child, struct resource *ires, int flags,
492101099Srwatson    driver_filter_t *filt, driver_intr_t *func, void *arg, void **cookiep)
493101099Srwatson{
494101099Srwatson	struct upa_softc *sc;
495101099Srwatson	u_long vec;
496101099Srwatson
497101099Srwatson	sc = device_get_softc(dev);
498101099Srwatson	/*
499101099Srwatson	 * Make sure the vector is fully specified and we registered
500101099Srwatson	 * our interrupt controller for it.
501101099Srwatson	 */
502101099Srwatson	vec = rman_get_start(ires);
503101099Srwatson	if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &upa_ic) {
504101099Srwatson		device_printf(dev, "invalid interrupt vector 0x%lx\n", vec);
505101099Srwatson		return (EINVAL);
506101099Srwatson	}
507101099Srwatson	return (bus_generic_setup_intr(dev, child, ires, flags, filt, func,
508101099Srwatson	    arg, cookiep));
509101099Srwatson}
510101099Srwatson
511101099Srwatsonstatic int
512101099Srwatsonupa_adjust_resource(device_t bus __unused, device_t child __unused,
513101099Srwatson    int type __unused, struct resource *r __unused, u_long start __unused,
514101099Srwatson    u_long end __unused)
515101099Srwatson{
516101099Srwatson
517101099Srwatson	return (ENXIO);
518101099Srwatson}
519101099Srwatson
520101099Srwatsonstatic struct resource_list *
521101099Srwatsonupa_get_resource_list(device_t dev, device_t child)
522101099Srwatson{
523101099Srwatson	struct upa_devinfo *udi;
524101099Srwatson
525101099Srwatson	udi = device_get_ivars(child);
526101099Srwatson	return (&udi->udi_rl);
527101099Srwatson}
528101099Srwatson
529101099Srwatsonstatic const struct ofw_bus_devinfo *
530101099Srwatsonupa_get_devinfo(device_t dev, device_t child)
531101099Srwatson{
532101099Srwatson	struct upa_devinfo *udi;
533101099Srwatson
534101099Srwatson	udi = device_get_ivars(child);
535101099Srwatson	return (&udi->udi_obdinfo);
536101099Srwatson}
537101099Srwatson
538101099Srwatsonstatic struct upa_devinfo *
539101099Srwatsonupa_setup_dinfo(device_t dev, struct upa_softc *sc, phandle_t node,
540101099Srwatson    uint32_t portid)
541101099Srwatson{
542101099Srwatson	struct upa_devinfo *udi;
543101099Srwatson	struct upa_regs *reg;
544101099Srwatson	uint32_t intr;
545101099Srwatson	int i, nreg;
546101099Srwatson
547101099Srwatson	udi = malloc(sizeof(*udi), M_DEVBUF, M_WAITOK | M_ZERO);
548101099Srwatson	if (ofw_bus_gen_setup_devinfo(&udi->udi_obdinfo, node) != 0) {
549101099Srwatson		free(udi, M_DEVBUF);
550101099Srwatson		return (NULL);
551101099Srwatson	}
552101099Srwatson	resource_list_init(&udi->udi_rl);
553101099Srwatson
554101099Srwatson	nreg = OF_getprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
555101099Srwatson	if (nreg == -1) {
556101099Srwatson		device_printf(dev, "<%s>: incomplete\n",
557101099Srwatson		    udi->udi_obdinfo.obd_name);
558101099Srwatson		goto fail;
559101099Srwatson	}
560101099Srwatson	for (i = 0; i < nreg; i++)
561101099Srwatson		resource_list_add(&udi->udi_rl, SYS_RES_MEMORY, i, reg[i].phys,
562101099Srwatson		    reg[i].phys + reg[i].size - 1, reg[i].size);
563101099Srwatson	free(reg, M_OFWPROP);
564101099Srwatson
565101099Srwatson	intr = INTMAP_VEC(sc->sc_ign, (UPA_INO_BASE + portid));
566101099Srwatson	resource_list_add(&udi->udi_rl, SYS_RES_IRQ, 0, intr, intr, 1);
567101099Srwatson
568101099Srwatson	return (udi);
569101099Srwatson
570101099Srwatson fail:
571101099Srwatson	upa_destroy_dinfo(udi);
572101099Srwatson	return (NULL);
573101099Srwatson}
574101099Srwatson
575101099Srwatsonstatic void
576101099Srwatsonupa_destroy_dinfo(struct upa_devinfo *dinfo)
577101099Srwatson{
578101099Srwatson
579101099Srwatson	resource_list_free(&dinfo->udi_rl);
580101099Srwatson	ofw_bus_gen_destroy_devinfo(&dinfo->udi_obdinfo);
581101099Srwatson	free(dinfo, M_DEVBUF);
582101099Srwatson}
583101099Srwatson
584101099Srwatsonstatic int
585101099Srwatsonupa_print_res(struct upa_devinfo *udi)
586101099Srwatson{
587101099Srwatson	int rv;
588101099Srwatson
589101099Srwatson	rv = 0;
590101099Srwatson	rv += resource_list_print_type(&udi->udi_rl, "mem", SYS_RES_MEMORY,
591101099Srwatson	    "%#lx");
592101099Srwatson	rv += resource_list_print_type(&udi->udi_rl, "irq", SYS_RES_IRQ,
593101099Srwatson	    "%ld");
594101099Srwatson	return (rv);
595101099Srwatson}
596101099Srwatson