bhndb.c revision 302408
1170530Ssam/*-
2178354Ssam * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3170530Ssam * All rights reserved.
4170530Ssam *
5170530Ssam * Redistribution and use in source and binary forms, with or without
6170530Ssam * modification, are permitted provided that the following conditions
7170530Ssam * are met:
8170530Ssam * 1. Redistributions of source code must retain the above copyright
9170530Ssam *    notice, this list of conditions and the following disclaimer,
10170530Ssam *    without modification.
11170530Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12170530Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13170530Ssam *    redistribution must be conditioned upon including a substantially
14170530Ssam *    similar Disclaimer requirement for further binary redistribution.
15170530Ssam *
16170530Ssam * NO WARRANTY
17170530Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18170530Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19170530Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20170530Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21170530Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22170530Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23170530Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24170530Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25170530Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26170530Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27170530Ssam * THE POSSIBILITY OF SUCH DAMAGES.
28170530Ssam */
29170530Ssam
30170530Ssam#include <sys/cdefs.h>
31170530Ssam__FBSDID("$FreeBSD: stable/11/sys/dev/bhnd/bhndb/bhndb.c 302106 2016-06-23 01:15:35Z adrian $");
32170530Ssam
33170530Ssam/*
34170530Ssam * Abstract BHND Bridge Device Driver
35170530Ssam *
36178354Ssam * Provides generic support for bridging from a parent bus (such as PCI) to
37170530Ssam * a BHND-compatible bus (e.g. bcma or siba).
38170530Ssam */
39170530Ssam
40170530Ssam#include <sys/param.h>
41170530Ssam#include <sys/kernel.h>
42170530Ssam#include <sys/bus.h>
43170530Ssam#include <sys/module.h>
44170530Ssam#include <sys/systm.h>
45170530Ssam
46170530Ssam#include <machine/bus.h>
47170530Ssam#include <sys/rman.h>
48170530Ssam#include <machine/resource.h>
49170530Ssam
50178354Ssam#include <dev/bhnd/bhndvar.h>
51170530Ssam#include <dev/bhnd/bhndreg.h>
52170530Ssam
53170530Ssam#include <dev/bhnd/cores/chipc/chipcreg.h>
54170530Ssam#include <dev/bhnd/nvram/bhnd_nvram.h>
55170530Ssam
56178354Ssam#include "bhnd_chipc_if.h"
57178354Ssam#include "bhnd_nvram_if.h"
58178354Ssam
59178354Ssam#include "bhndbvar.h"
60178354Ssam#include "bhndb_bus_if.h"
61178354Ssam#include "bhndb_hwdata.h"
62178354Ssam#include "bhndb_private.h"
63178354Ssam
64178354Ssam/* Debugging flags */
65178354Ssamstatic u_long bhndb_debug = 0;
66178354SsamTUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug);
67178354Ssam
68178354Ssamenum {
69178354Ssam	BHNDB_DEBUG_PRIO = 1 << 0,
70178354Ssam};
71178354Ssam
72178354Ssam#define	BHNDB_DEBUG(_type)	(BHNDB_DEBUG_ ## _type & bhndb_debug)
73170530Ssam
74170530Ssamstatic bool			 bhndb_hw_matches(device_t *devlist,
75170530Ssam				     int num_devs,
76170530Ssam				     const struct bhndb_hw *hw);
77170530Ssam
78170530Ssamstatic int			 bhndb_initialize_region_cfg(
79170530Ssam				     struct bhndb_softc *sc, device_t *devs,
80170530Ssam				     int ndevs,
81173273Ssam				     const struct bhndb_hw_priority *table,
82173273Ssam				     struct bhndb_resources *r);
83173273Ssam
84173273Ssamstatic int			 bhndb_find_hwspec(struct bhndb_softc *sc,
85173273Ssam				     device_t *devs, int ndevs,
86178354Ssam				     const struct bhndb_hw **hw);
87178354Ssam
88178354Ssamstatic int			 bhndb_read_chipid(struct bhndb_softc *sc,
89173273Ssam				     const struct bhndb_hwcfg *cfg,
90178354Ssam				     struct bhnd_chipid *result);
91178354Ssam
92178354Ssambhndb_addrspace			 bhndb_get_addrspace(struct bhndb_softc *sc,
93178354Ssam				     device_t child);
94178354Ssam
95178354Ssamstatic struct rman		*bhndb_get_rman(struct bhndb_softc *sc,
96178354Ssam				     device_t child, int type);
97178354Ssam
98178354Ssamstatic int			 bhndb_init_child_resource(struct resource *r,
99178354Ssam				     struct resource *parent,
100178354Ssam				     bhnd_size_t offset,
101178354Ssam				     bhnd_size_t size);
102178354Ssam
103170530Ssamstatic int			 bhndb_activate_static_region(
104178354Ssam				     struct bhndb_softc *sc,
105178354Ssam				     struct bhndb_region *region,
106170530Ssam				     device_t child, int type, int rid,
107170530Ssam				     struct resource *r);
108170530Ssam
109170530Ssamstatic int			 bhndb_try_activate_resource(
110170530Ssam				     struct bhndb_softc *sc, device_t child,
111170530Ssam				     int type, int rid, struct resource *r,
112170530Ssam				     bool *indirect);
113170530Ssam
114170530Ssam
115170530Ssam/**
116170530Ssam * Default bhndb(4) implementation of DEVICE_PROBE().
117170530Ssam *
118170530Ssam * This function provides the default bhndb implementation of DEVICE_PROBE(),
119170530Ssam * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge().
120170530Ssam */
121170530Ssamint
122170530Ssambhndb_generic_probe(device_t dev)
123178354Ssam{
124170530Ssam	return (BUS_PROBE_NOWILDCARD);
125170530Ssam}
126170530Ssam
127170530Ssamstatic void
128173273Ssambhndb_probe_nomatch(device_t dev, device_t child)
129173273Ssam{
130178354Ssam	const char *name;
131173273Ssam
132178354Ssam	name = device_get_name(child);
133178354Ssam	if (name == NULL)
134178354Ssam		name = "unknown device";
135178354Ssam
136173273Ssam	device_printf(dev, "<%s> (no driver attached)\n", name);
137178354Ssam}
138178354Ssam
139178354Ssamstatic int
140178354Ssambhndb_print_child(device_t dev, device_t child)
141178354Ssam{
142178354Ssam	struct bhndb_softc	*sc;
143178354Ssam	struct resource_list	*rl;
144178354Ssam	int			 retval = 0;
145178354Ssam
146178354Ssam	sc = device_get_softc(dev);
147178354Ssam
148178354Ssam	retval += bus_print_child_header(dev, child);
149178354Ssam
150178354Ssam	rl = BUS_GET_RESOURCE_LIST(dev, child);
151178354Ssam	if (rl != NULL) {
152178354Ssam		retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
153170530Ssam		    "%#jx");
154173273Ssam		retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ,
155173273Ssam		    "%jd");
156170530Ssam	}
157170530Ssam
158178354Ssam	retval += bus_print_child_domain(dev, child);
159178354Ssam	retval += bus_print_child_footer(dev, child);
160178354Ssam
161178354Ssam	return (retval);
162178354Ssam}
163173273Ssam
164178354Ssamstatic int
165178354Ssambhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf,
166178354Ssam    size_t buflen)
167178354Ssam{
168170530Ssam	*buf = '\0';
169170530Ssam	return (0);
170178354Ssam}
171178354Ssam
172178354Ssamstatic int
173178354Ssambhndb_child_location_str(device_t dev, device_t child, char *buf,
174178354Ssam    size_t buflen)
175178354Ssam{
176170530Ssam	struct bhndb_softc *sc;
177178354Ssam
178178354Ssam	sc = device_get_softc(dev);
179178354Ssam
180170530Ssam	snprintf(buf, buflen, "base=0x%llx",
181170530Ssam	    (unsigned long long) sc->chipid.enum_addr);
182170530Ssam	return (0);
183178354Ssam}
184170530Ssam
185170530Ssam/**
186170530Ssam * Return true if @p devlist matches the @p hw specification.
187170530Ssam *
188170530Ssam * @param devlist A device table to match against.
189170530Ssam * @param num_devs The number of devices in @p devlist.
190170530Ssam * @param hw The hardware description to be matched against.
191170530Ssam */
192170530Ssamstatic bool
193170530Ssambhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw)
194170530Ssam{
195170530Ssam	for (u_int i = 0; i < hw->num_hw_reqs; i++) {
196172226Ssam		const struct bhnd_core_match	*match;
197172226Ssam		struct bhnd_core_info		 ci;
198170530Ssam		bool				 found;
199170530Ssam
200178354Ssam		match =  &hw->hw_reqs[i];
201170530Ssam		found = false;
202170530Ssam
203170530Ssam		for (int d = 0; d < num_devs; d++) {
204170530Ssam			ci = bhnd_get_core_info(devlist[d]);
205170530Ssam			if (!bhnd_core_matches(&ci, match))
206170530Ssam				continue;
207170530Ssam
208170530Ssam			found = true;
209170530Ssam			break;
210170530Ssam		}
211170530Ssam
212170530Ssam		if (!found)
213170530Ssam			return (false);
214170530Ssam	}
215170530Ssam
216170530Ssam	return (true);
217170530Ssam}
218170530Ssam
219170530Ssam/**
220173273Ssam * Initialize the region maps and priority configuration in @p r using
221170530Ssam * the provided priority @p table and the set of devices attached to
222170530Ssam * the bridged @p bus_dev .
223170530Ssam *
224170530Ssam * @param sc The bhndb device state.
225170530Ssam * @param devs All devices enumerated on the bridged bhnd bus.
226170530Ssam * @param ndevs The length of @p devs.
227170530Ssam * @param table Hardware priority table to be used to determine the relative
228170530Ssam * priorities of per-core port resources.
229170530Ssam * @param r The resource state to be configured.
230170530Ssam */
231170530Ssamstatic int
232170530Ssambhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
233170530Ssam    const struct bhndb_hw_priority *table, struct bhndb_resources *r)
234170530Ssam{
235178354Ssam	const struct bhndb_hw_priority	*hp;
236173462Ssam	bhnd_addr_t			 addr;
237170530Ssam	bhnd_size_t			 size;
238170530Ssam	size_t				 prio_low, prio_default, prio_high;
239170530Ssam	int				 error;
240170530Ssam
241170530Ssam	/* The number of port regions per priority band that must be accessible
242178354Ssam	 * via dynamic register windows */
243170530Ssam	prio_low = 0;
244170530Ssam	prio_default = 0;
245170530Ssam	prio_high = 0;
246170530Ssam
247170530Ssam	/*
248170530Ssam	 * Register bridge regions covering all statically mapped ports.
249170530Ssam	 */
250170530Ssam	for (int i = 0; i < ndevs; i++) {
251170530Ssam		const struct bhndb_regwin	*regw;
252170530Ssam		device_t			 child;
253178354Ssam
254173462Ssam		child = devs[i];
255178354Ssam
256170530Ssam		for (regw = r->cfg->register_windows;
257170530Ssam		    regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
258173462Ssam		{
259170530Ssam			/* Only core windows are supported */
260170530Ssam			if (regw->win_type != BHNDB_REGWIN_T_CORE)
261170530Ssam				continue;
262178354Ssam
263170530Ssam			/* Skip non-applicable register windows. */
264170530Ssam			if (!bhndb_regwin_matches_device(regw, child))
265178354Ssam				continue;
266170530Ssam
267170530Ssam			/* Fetch the base address of the mapped port. */
268170530Ssam			error = bhnd_get_region_addr(child,
269178354Ssam			    regw->d.core.port_type, regw->d.core.port,
270170530Ssam			    regw->d.core.region, &addr, &size);
271170530Ssam			if (error)
272170530Ssam			    return (error);
273170530Ssam
274170530Ssam			/*
275170530Ssam			 * Always defer to the register window's size.
276170530Ssam			 *
277170530Ssam			 * If the port size is smaller than the window size,
278170530Ssam			 * this ensures that we fully utilize register windows
279170530Ssam			 * larger than the referenced port.
280170530Ssam			 *
281170530Ssam			 * If the port size is larger than the window size, this
282170530Ssam			 * ensures that we do not directly map the allocations
283170530Ssam			 * within the region to a too-small window.
284170530Ssam			 */
285170530Ssam			size = regw->win_size;
286170530Ssam
287170530Ssam			/*
288170530Ssam			 * Add to the bus region list.
289170530Ssam			 *
290170530Ssam			 * The window priority for a statically mapped
291170530Ssam			 * region is always HIGH.
292170530Ssam			 */
293170530Ssam			error = bhndb_add_resource_region(r, addr, size,
294170530Ssam			    BHNDB_PRIORITY_HIGH, regw);
295170530Ssam			if (error)
296170530Ssam				return (error);
297170530Ssam		}
298170530Ssam	}
299170530Ssam
300170530Ssam	/*
301170530Ssam	 * Perform priority accounting and register bridge regions for all
302170530Ssam	 * ports defined in the priority table
303170530Ssam	 */
304170530Ssam	for (int i = 0; i < ndevs; i++) {
305170530Ssam		struct bhndb_region	*region;
306178354Ssam		device_t		 child;
307178354Ssam
308178354Ssam		child = devs[i];
309178354Ssam
310178354Ssam		/*
311178354Ssam		 * Skip priority accounting for cores that ...
312178354Ssam		 */
313178354Ssam
314178354Ssam		/* ... do not require bridge resources */
315178354Ssam		if (bhnd_is_hw_disabled(child) || !device_is_enabled(child))
316178354Ssam			continue;
317178354Ssam
318178354Ssam		/* ... do not have a priority table entry */
319178354Ssam		hp = bhndb_hw_priority_find_device(table, child);
320178354Ssam		if (hp == NULL)
321178354Ssam			continue;
322178354Ssam
323178354Ssam		/* ... are explicitly disabled in the priority table. */
324178354Ssam		if (hp->priority == BHNDB_PRIORITY_NONE)
325178354Ssam			continue;
326170530Ssam
327170530Ssam		/* Determine the number of dynamic windows required and
328170530Ssam		 * register their bus_region entries. */
329170530Ssam		for (u_int i = 0; i < hp->num_ports; i++) {
330170530Ssam			const struct bhndb_port_priority *pp;
331170530Ssam
332178354Ssam			pp = &hp->ports[i];
333170530Ssam
334170530Ssam			/* Skip ports not defined on this device */
335170530Ssam			if (!bhnd_is_region_valid(child, pp->type, pp->port,
336170530Ssam			    pp->region))
337170530Ssam			{
338170530Ssam				continue;
339170530Ssam			}
340170530Ssam
341170530Ssam			/* Fetch the address+size of the mapped port. */
342170530Ssam			error = bhnd_get_region_addr(child, pp->type, pp->port,
343170530Ssam			    pp->region, &addr, &size);
344170530Ssam			if (error)
345170530Ssam			    return (error);
346178354Ssam
347170530Ssam			/* Skip ports with an existing static mapping */
348170530Ssam			region = bhndb_find_resource_region(r, addr, size);
349170530Ssam			if (region != NULL && region->static_regwin != NULL)
350170530Ssam				continue;
351170530Ssam
352170530Ssam			/* Define a dynamic region for this port */
353170530Ssam			error = bhndb_add_resource_region(r, addr, size,
354170530Ssam			    pp->priority, NULL);
355170530Ssam			if (error)
356170530Ssam				return (error);
357170530Ssam
358170530Ssam			/* Update port mapping counts */
359170530Ssam			switch (pp->priority) {
360178354Ssam			case BHNDB_PRIORITY_NONE:
361170530Ssam				break;
362170530Ssam			case BHNDB_PRIORITY_LOW:
363170530Ssam				prio_low++;
364170530Ssam				break;
365170530Ssam			case BHNDB_PRIORITY_DEFAULT:
366170530Ssam				prio_default++;
367170530Ssam				break;
368170530Ssam			case BHNDB_PRIORITY_HIGH:
369170530Ssam				prio_high++;
370170530Ssam				break;
371170530Ssam			}
372170530Ssam		}
373170530Ssam	}
374170530Ssam
375170530Ssam	/* Determine the minimum priority at which we'll allocate direct
376170530Ssam	 * register windows from our dynamic pool */
377170530Ssam	size_t prio_total = prio_low + prio_default + prio_high;
378170530Ssam	if (prio_total <= r->dwa_count) {
379170530Ssam		/* low+default+high priority regions get windows */
380170530Ssam		r->min_prio = BHNDB_PRIORITY_LOW;
381170530Ssam
382170530Ssam	} else if (prio_default + prio_high <= r->dwa_count) {
383170530Ssam		/* default+high priority regions get windows */
384170530Ssam		r->min_prio = BHNDB_PRIORITY_DEFAULT;
385170530Ssam
386170530Ssam	} else {
387170530Ssam		/* high priority regions get windows */
388170530Ssam		r->min_prio = BHNDB_PRIORITY_HIGH;
389170530Ssam	}
390178354Ssam
391170530Ssam	if (BHNDB_DEBUG(PRIO)) {
392173273Ssam		struct bhndb_region	*region;
393173273Ssam		const char		*direct_msg, *type_msg;
394173273Ssam		bhndb_priority_t	 prio, prio_min;
395173273Ssam
396173273Ssam		prio_min = r->min_prio;
397178354Ssam		device_printf(sc->dev, "min_prio: %d\n", prio_min);
398170530Ssam
399170530Ssam		STAILQ_FOREACH(region, &r->bus_regions, link) {
400173273Ssam			prio = region->priority;
401170530Ssam
402173273Ssam			direct_msg = prio >= prio_min ? "direct" : "indirect";
403170530Ssam			type_msg = region->static_regwin ? "static" : "dynamic";
404170530Ssam
405173273Ssam			device_printf(sc->dev, "region 0x%llx+0x%llx priority "
406170530Ssam			    "%u %s/%s\n",
407178354Ssam			    (unsigned long long) region->addr,
408170530Ssam			    (unsigned long long) region->size,
409170530Ssam			    region->priority,
410170530Ssam			    direct_msg, type_msg);
411173273Ssam		}
412170530Ssam	}
413170530Ssam
414170530Ssam	return (0);
415170530Ssam}
416170530Ssam
417173273Ssam/**
418178354Ssam * Find a hardware specification for @p dev.
419173273Ssam *
420170530Ssam * @param sc The bhndb device state.
421173273Ssam * @param devs All devices enumerated on the bridged bhnd bus.
422170530Ssam * @param ndevs The length of @p devs.
423170530Ssam * @param[out] hw On success, the matched hardware specification.
424170530Ssam * with @p dev.
425173273Ssam *
426170530Ssam * @retval 0 success
427170530Ssam * @retval non-zero if an error occurs fetching device info for comparison.
428173273Ssam */
429173273Ssamstatic int
430173273Ssambhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs,
431173273Ssam    const struct bhndb_hw **hw)
432173273Ssam{
433173273Ssam	const struct bhndb_hw	*next, *hw_table;
434173273Ssam
435173273Ssam	/* Search for the first matching hardware config. */
436178354Ssam	hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev);
437173273Ssam	for (next = hw_table; next->hw_reqs != NULL; next++) {
438173273Ssam		if (!bhndb_hw_matches(devs, ndevs, next))
439173273Ssam			continue;
440173273Ssam
441173273Ssam		/* Found */
442173273Ssam		*hw = next;
443173273Ssam		return (0);
444173273Ssam	}
445173273Ssam
446173273Ssam	return (ENOENT);
447173273Ssam}
448173273Ssam
449173273Ssam/**
450173273Ssam * Read the ChipCommon identification data for this device.
451173273Ssam *
452173273Ssam * @param sc bhndb device state.
453173273Ssam * @param cfg The hardware configuration to use when mapping the ChipCommon
454173273Ssam * registers.
455178354Ssam * @param[out] result the chip identification data.
456173273Ssam *
457173273Ssam * @retval 0 success
458173273Ssam * @retval non-zero if the ChipCommon identification data could not be read.
459173273Ssam */
460173273Ssamstatic int
461173273Ssambhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg,
462173273Ssam    struct bhnd_chipid *result)
463173273Ssam{
464173273Ssam	const struct bhnd_chipid	*parent_cid;
465173273Ssam	const struct bhndb_regwin	*cc_win;
466173273Ssam	struct resource_spec		 rs;
467173273Ssam	int				 error;
468173273Ssam
469173273Ssam	/* Let our parent device override the discovery process */
470178354Ssam	parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev);
471178354Ssam	if (parent_cid != NULL) {
472178354Ssam		*result = *parent_cid;
473178354Ssam		return (0);
474173273Ssam	}
475173273Ssam
476173273Ssam	/* Find a register window we can use to map the first CHIPC_CHIPID_SIZE
477173273Ssam	 * of ChipCommon registers. */
478173273Ssam	cc_win = bhndb_regwin_find_best(cfg->register_windows,
479173273Ssam	    BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE);
480173273Ssam	if (cc_win == NULL) {
481173273Ssam		device_printf(sc->dev, "no chipcommon register window\n");
482173273Ssam		return (0);
483173273Ssam	}
484173273Ssam
485173273Ssam	/* We can assume a device without a static ChipCommon window uses the
486173273Ssam	 * default ChipCommon address. */
487178354Ssam	if (cc_win->win_type == BHNDB_REGWIN_T_DYN) {
488173273Ssam		error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win,
489173273Ssam		    BHND_DEFAULT_CHIPC_ADDR);
490173273Ssam
491173273Ssam		if (error) {
492173273Ssam			device_printf(sc->dev, "failed to set chipcommon "
493173273Ssam			    "register window\n");
494173273Ssam			return (error);
495173273Ssam		}
496173273Ssam	}
497173273Ssam
498173273Ssam	/* Let the default bhnd implemenation alloc/release the resource and
499170530Ssam	 * perform the read */
500170530Ssam	rs.type = cc_win->res.type;
501170530Ssam	rs.rid = cc_win->res.rid;
502173273Ssam	rs.flags = RF_ACTIVE;
503170530Ssam
504170530Ssam	return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset,
505170530Ssam	    result));
506170530Ssam}
507170530Ssam
508170530Ssam/**
509170530Ssam * Helper function that must be called by subclass bhndb(4) drivers
510170530Ssam * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4)
511173273Ssam * APIs on the bridge device.
512173273Ssam *
513178354Ssam * @param dev The bridge device to attach.
514170530Ssam * @param bridge_devclass The device class of the bridging core. This is used
515170530Ssam * to automatically detect the bridge core, and to disable additional bridge
516170530Ssam * cores (e.g. PCMCIA on a PCIe device).
517170530Ssam */
518170530Ssamint
519170530Ssambhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
520170530Ssam{
521170530Ssam	struct bhndb_devinfo		*dinfo;
522170530Ssam	struct bhndb_softc		*sc;
523170530Ssam	const struct bhndb_hwcfg	*cfg;
524170530Ssam	int				 error;
525170530Ssam
526173273Ssam	sc = device_get_softc(dev);
527173273Ssam	sc->dev = dev;
528173273Ssam	sc->parent_dev = device_get_parent(dev);
529173273Ssam	sc->bridge_class = bridge_devclass;
530173273Ssam
531170530Ssam	BHNDB_LOCK_INIT(sc);
532170530Ssam
533170530Ssam	/* Read our chip identification data */
534170530Ssam	cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev);
535170530Ssam	if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid)))
536173273Ssam		return (error);
537170530Ssam
538182827Ssam	/* Populate generic resource allocation state. */
539182827Ssam	sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg);
540182827Ssam	if (sc->bus_res == NULL) {
541182827Ssam		return (ENXIO);
542182827Ssam	}
543182827Ssam
544182827Ssam	/* Attach our bridged bus device */
545182827Ssam	sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1);
546182827Ssam	if (sc->bus_dev == NULL) {
547182827Ssam		error = ENXIO;
548182827Ssam		goto failed;
549182827Ssam	}
550182827Ssam
551182827Ssam	/* Configure address space */
552182827Ssam	dinfo = device_get_ivars(sc->bus_dev);
553173273Ssam	dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
554173273Ssam
555170530Ssam	/* Finish attach */
556170530Ssam	return (bus_generic_attach(dev));
557170530Ssam
558170530Ssamfailed:
559170530Ssam	BHNDB_LOCK_DESTROY(sc);
560170530Ssam
561170530Ssam	if (sc->bus_res != NULL)
562170530Ssam		bhndb_free_resources(sc->bus_res);
563170530Ssam
564170530Ssam	return (error);
565170530Ssam}
566173273Ssam
567170530Ssam/**
568170530Ssam * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG().
569170530Ssam *
570170530Ssam * This function provides the default bhndb implementation of
571170530Ssam * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver
572170530Ssam * overriding BHNDB_INIT_FULL_CONFIG().
573173273Ssam *
574170530Ssam * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final
575170530Ssam * bridge configuration based on the hardware information enumerated by the
576170530Ssam * child bus, and will reset all resource allocation state on the bridge.
577173273Ssam *
578170530Ssam * When calling this method:
579170530Ssam * - Any bus resources previously allocated by @p child must be deallocated.
580170530Ssam * - The @p child bus must have performed initial enumeration -- but not
581173273Ssam *   probe or attachment -- of its children.
582170530Ssam */
583173273Ssamint
584173273Ssambhndb_generic_init_full_config(device_t dev, device_t child,
585173273Ssam    const struct bhndb_hw_priority *hw_prio_table)
586173273Ssam{
587173273Ssam	struct bhndb_softc		*sc;
588173273Ssam	const struct bhndb_hw		*hw;
589173273Ssam	struct bhndb_resources		*r;
590173273Ssam	device_t			*devs;
591173273Ssam	device_t			 hostb;
592173273Ssam	int				 ndevs;
593173273Ssam	int				 error;
594173273Ssam
595173273Ssam	sc = device_get_softc(dev);
596173273Ssam	hostb = NULL;
597170530Ssam
598173273Ssam	/* Fetch the full set of bhnd-attached cores */
599173273Ssam	if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) {
600173273Ssam		device_printf(sc->dev, "unable to get children\n");
601173273Ssam		return (error);
602173273Ssam	}
603170530Ssam
604173273Ssam	/* Find our host bridge device */
605173273Ssam	hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child);
606173273Ssam	if (hostb == NULL) {
607173273Ssam		device_printf(sc->dev, "no host bridge core found\n");
608173273Ssam		error = ENODEV;
609173273Ssam		goto cleanup;
610173273Ssam	}
611173273Ssam
612178354Ssam	/* Find our full register window configuration */
613173273Ssam	if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) {
614173273Ssam		device_printf(sc->dev, "unable to identify device, "
615173273Ssam			" using generic bridge resource definitions\n");
616173273Ssam		error = 0;
617173273Ssam		goto cleanup;
618173273Ssam	}
619173273Ssam
620173273Ssam	if (bootverbose || BHNDB_DEBUG(PRIO))
621173273Ssam		device_printf(sc->dev, "%s resource configuration\n", hw->name);
622173273Ssam
623173273Ssam	/* Release existing resource state */
624173273Ssam	BHNDB_LOCK(sc);
625173273Ssam	bhndb_free_resources(sc->bus_res);
626173273Ssam	sc->bus_res = NULL;
627173273Ssam	BHNDB_UNLOCK(sc);
628173273Ssam
629173273Ssam	/* Allocate new resource state */
630173273Ssam	r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg);
631178354Ssam	if (r == NULL) {
632173273Ssam		error = ENXIO;
633178354Ssam		goto cleanup;
634173273Ssam	}
635173273Ssam
636173273Ssam	/* Initialize our resource priority configuration */
637173273Ssam	error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r);
638173273Ssam	if (error) {
639178354Ssam		bhndb_free_resources(r);
640173273Ssam		goto cleanup;
641173273Ssam	}
642173273Ssam
643173273Ssam	/* Update our bridge state */
644173273Ssam	BHNDB_LOCK(sc);
645173273Ssam	sc->bus_res = r;
646173273Ssam	sc->hostb_dev = hostb;
647173273Ssam	BHNDB_UNLOCK(sc);
648173273Ssam
649173273Ssamcleanup:
650173273Ssam	free(devs, M_TEMP);
651178354Ssam	return (error);
652173273Ssam}
653170530Ssam
654173273Ssam/**
655170530Ssam * Default bhndb(4) implementation of DEVICE_DETACH().
656178354Ssam *
657170530Ssam * This function detaches any child devices, and if successful, releases all
658173273Ssam * resources held by the bridge device.
659173273Ssam */
660173273Ssamint
661173273Ssambhndb_generic_detach(device_t dev)
662173273Ssam{
663173273Ssam	struct bhndb_softc	*sc;
664173273Ssam	int			 error;
665173273Ssam
666173273Ssam	sc = device_get_softc(dev);
667173273Ssam
668173273Ssam	/* Detach children */
669173273Ssam	if ((error = bus_generic_detach(dev)))
670170530Ssam		return (error);
671170530Ssam
672173273Ssam	/* Clean up our driver state. */
673173273Ssam	bhndb_free_resources(sc->bus_res);
674170530Ssam
675178354Ssam	BHNDB_LOCK_DESTROY(sc);
676173273Ssam
677178354Ssam	return (0);
678173273Ssam}
679173273Ssam
680173273Ssam/**
681173273Ssam * Default bhndb(4) implementation of DEVICE_SUSPEND().
682178354Ssam *
683173273Ssam * This function calls bus_generic_suspend() (or implements equivalent
684170530Ssam * behavior).
685173273Ssam */
686170530Ssamint
687173273Ssambhndb_generic_suspend(device_t dev)
688173273Ssam{
689170530Ssam	return (bus_generic_suspend(dev));
690170530Ssam}
691170530Ssam
692170530Ssam/**
693170530Ssam * Default bhndb(4) implementation of DEVICE_RESUME().
694170530Ssam *
695173273Ssam * This function calls bus_generic_resume() (or implements equivalent
696170530Ssam * behavior).
697170530Ssam */
698170530Ssamint
699170530Ssambhndb_generic_resume(device_t dev)
700178354Ssam{
701170530Ssam	struct bhndb_softc	*sc;
702170530Ssam	struct bhndb_resources	*bus_res;
703170530Ssam	struct bhndb_dw_alloc	*dwa;
704170530Ssam	int			 error;
705170530Ssam
706173273Ssam	sc = device_get_softc(dev);
707173273Ssam	bus_res = sc->bus_res;
708178354Ssam
709173273Ssam	/* Guarantee that all in-use dynamic register windows are mapped to
710173273Ssam	 * their previously configured target address. */
711178354Ssam	BHNDB_LOCK(sc);
712173273Ssam	for (size_t i = 0; i < bus_res->dwa_count; i++) {
713173273Ssam		dwa = &bus_res->dw_alloc[i];
714170530Ssam
715170530Ssam		/* Skip regions that were not previously used */
716170530Ssam		if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0)
717170530Ssam			continue;
718170530Ssam
719170530Ssam		/* Otherwise, ensure the register window is correct before
720170530Ssam		 * any children attempt MMIO */
721170530Ssam		error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target);
722178354Ssam		if (error)
723170530Ssam			break;
724170530Ssam	}
725178354Ssam	BHNDB_UNLOCK(sc);
726170530Ssam
727170530Ssam	/* Error restoring hardware state; children cannot be safely resumed */
728178354Ssam	if (error) {
729170530Ssam		device_printf(dev, "Unable to restore hardware configuration; "
730173273Ssam		    "cannot resume: %d\n", error);
731173273Ssam		return (error);
732170530Ssam	}
733170530Ssam
734173273Ssam	return (bus_generic_resume(dev));
735170530Ssam}
736173273Ssam
737173273Ssam/**
738170530Ssam * Default implementation of BHNDB_SUSPEND_RESOURCE.
739178354Ssam */
740173273Ssamstatic void
741170530Ssambhndb_suspend_resource(device_t dev, device_t child, int type,
742173273Ssam    struct resource *r)
743173273Ssam{
744178354Ssam	struct bhndb_softc	*sc;
745173273Ssam	struct bhndb_dw_alloc	*dwa;
746173273Ssam
747173273Ssam	sc = device_get_softc(dev);
748173273Ssam
749173273Ssam	// TODO: IRQs?
750173273Ssam	if (type != SYS_RES_MEMORY)
751173273Ssam		return;
752173273Ssam
753173273Ssam	BHNDB_LOCK(sc);
754170530Ssam	dwa = bhndb_dw_find_resource(sc->bus_res, r);
755173273Ssam	if (dwa == NULL) {
756170530Ssam		BHNDB_UNLOCK(sc);
757173273Ssam		return;
758173273Ssam	}
759170530Ssam
760178354Ssam	if (BHNDB_DEBUG(PRIO))
761173273Ssam		device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n",
762173273Ssam		    type, rman_get_start(r), rman_get_size(r));
763173273Ssam
764173273Ssam	/* Release the resource's window reference */
765173273Ssam	bhndb_dw_release(sc->bus_res, dwa, r);
766173273Ssam	BHNDB_UNLOCK(sc);
767178354Ssam}
768173273Ssam
769170530Ssam/**
770170530Ssam * Default implementation of BHNDB_RESUME_RESOURCE.
771170530Ssam */
772170530Ssamstatic int
773170530Ssambhndb_resume_resource(device_t dev, device_t child, int type,
774170530Ssam    struct resource *r)
775170530Ssam{
776170530Ssam	struct bhndb_softc	*sc;
777170530Ssam
778170530Ssam	sc = device_get_softc(dev);
779170530Ssam
780170530Ssam	// TODO: IRQs?
781170530Ssam	if (type != SYS_RES_MEMORY)
782170530Ssam		return (0);
783173273Ssam
784173273Ssam	/* Inactive resources don't require reallocation of bridge resources */
785173273Ssam	if (!(rman_get_flags(r) & RF_ACTIVE))
786173273Ssam		return (0);
787173273Ssam
788173273Ssam	if (BHNDB_DEBUG(PRIO))
789173273Ssam		device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n",
790173273Ssam		    type, rman_get_start(r), rman_get_size(r));
791170530Ssam
792170530Ssam	return (bhndb_try_activate_resource(sc, rman_get_device(r), type,
793170530Ssam	    rman_get_rid(r), r, NULL));
794170530Ssam}
795173273Ssam
796170530Ssam
797173273Ssam/**
798170530Ssam * Default bhndb(4) implementation of BUS_READ_IVAR().
799170530Ssam */
800170530Ssamstatic int
801170530Ssambhndb_read_ivar(device_t dev, device_t child, int index,
802170530Ssam    uintptr_t *result)
803170530Ssam{
804170530Ssam	return (ENOENT);
805170530Ssam}
806170530Ssam
807170530Ssam/**
808170530Ssam * Default bhndb(4) implementation of BUS_WRITE_IVAR().
809170530Ssam */
810170530Ssamstatic int
811170530Ssambhndb_write_ivar(device_t dev, device_t child, int index,
812170530Ssam    uintptr_t value)
813170530Ssam{
814170530Ssam	return (ENOENT);
815173273Ssam}
816173273Ssam
817173273Ssam/**
818173273Ssam * Return the address space for the given @p child device.
819173273Ssam */
820170530Ssambhndb_addrspace
821178354Ssambhndb_get_addrspace(struct bhndb_softc *sc, device_t child)
822178354Ssam{
823173273Ssam	struct bhndb_devinfo	*dinfo;
824173273Ssam	device_t		 imd_dev;
825173273Ssam
826173273Ssam	/* Find the directly attached parent of the requesting device */
827170530Ssam	imd_dev = child;
828170530Ssam	while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev)
829170530Ssam		imd_dev = device_get_parent(imd_dev);
830170530Ssam
831170530Ssam	if (imd_dev == NULL)
832173273Ssam		panic("bhndb address space request for non-child device %s\n",
833173273Ssam		     device_get_nameunit(child));
834170530Ssam
835170530Ssam	dinfo = device_get_ivars(imd_dev);
836178354Ssam	return (dinfo->addrspace);
837178354Ssam}
838178354Ssam
839178354Ssam/**
840178354Ssam * Return the rman instance for a given resource @p type, if any.
841178354Ssam *
842178354Ssam * @param sc The bhndb device state.
843178354Ssam * @param child The requesting child.
844178354Ssam * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...)
845178354Ssam */
846178354Ssamstatic struct rman *
847178354Ssambhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
848178354Ssam{
849178354Ssam	switch (bhndb_get_addrspace(sc, child)) {
850178354Ssam	case BHNDB_ADDRSPACE_NATIVE:
851178354Ssam		switch (type) {
852178354Ssam		case SYS_RES_MEMORY:
853178354Ssam			return (&sc->bus_res->ht_mem_rman);
854178354Ssam		case SYS_RES_IRQ:
855178354Ssam			return (NULL);
856178354Ssam		default:
857178354Ssam			return (NULL);
858178354Ssam		};
859178354Ssam
860178354Ssam	case BHNDB_ADDRSPACE_BRIDGED:
861178354Ssam		switch (type) {
862178354Ssam		case SYS_RES_MEMORY:
863178354Ssam			return (&sc->bus_res->br_mem_rman);
864178354Ssam		case SYS_RES_IRQ:
865178354Ssam			// TODO
866178354Ssam			// return &sc->irq_rman;
867178354Ssam			return (NULL);
868178354Ssam		default:
869178354Ssam			return (NULL);
870178354Ssam		};
871178354Ssam	}
872178354Ssam
873178354Ssam	/* Quieten gcc */
874178354Ssam	return (NULL);
875173273Ssam}
876173273Ssam
877173273Ssam/**
878173273Ssam * Default implementation of BUS_ADD_CHILD()
879173273Ssam */
880173273Ssamstatic device_t
881173273Ssambhndb_add_child(device_t dev, u_int order, const char *name, int unit)
882173273Ssam{
883173273Ssam	struct bhndb_devinfo	*dinfo;
884173273Ssam	device_t		 child;
885173273Ssam
886173273Ssam	child = device_add_child_ordered(dev, order, name, unit);
887173273Ssam	if (child == NULL)
888173273Ssam		return (NULL);
889173273Ssam
890173273Ssam	dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT);
891173273Ssam	if (dinfo == NULL) {
892173273Ssam		device_delete_child(dev, child);
893173273Ssam		return (NULL);
894173273Ssam	}
895173273Ssam
896173273Ssam	dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
897173273Ssam	resource_list_init(&dinfo->resources);
898173273Ssam
899173273Ssam	device_set_ivars(child, dinfo);
900173273Ssam
901173273Ssam	return (child);
902173273Ssam}
903173273Ssam
904173273Ssam/**
905173273Ssam * Default implementation of BUS_CHILD_DELETED().
906173273Ssam */
907173273Ssamstatic void
908173273Ssambhndb_child_deleted(device_t dev, device_t child)
909173273Ssam{
910173273Ssam	struct bhndb_devinfo *dinfo = device_get_ivars(child);
911173273Ssam	if (dinfo != NULL) {
912173273Ssam		resource_list_free(&dinfo->resources);
913173273Ssam		free(dinfo, M_BHND);
914173273Ssam	}
915173273Ssam
916173273Ssam	device_set_ivars(child, NULL);
917173273Ssam}
918173273Ssam
919173273Ssam/**
920173273Ssam * Default implementation of BHNDB_GET_CHIPID().
921173273Ssam */
922173273Ssamstatic const struct bhnd_chipid *
923173273Ssambhndb_get_chipid(device_t dev, device_t child)
924173273Ssam{
925173273Ssam	struct bhndb_softc *sc = device_get_softc(dev);
926173273Ssam	return (&sc->chipid);
927178354Ssam}
928173273Ssam
929173273Ssam
930173273Ssam/**
931178354Ssam * Default implementation of BHNDB_IS_HW_DISABLED().
932173273Ssam */
933173273Ssamstatic bool
934173273Ssambhndb_is_hw_disabled(device_t dev, device_t child) {
935173273Ssam	struct bhndb_softc	*sc;
936173273Ssam	struct bhnd_core_info	 core;
937173273Ssam
938173273Ssam	sc = device_get_softc(dev);
939173273Ssam
940178354Ssam	/* Requestor must be attached to the bhnd bus */
941178354Ssam	if (device_get_parent(child) != sc->bus_dev) {
942173273Ssam		return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child));
943173273Ssam	}
944178354Ssam
945173273Ssam	/* Fetch core info */
946173273Ssam	core = bhnd_get_core_info(child);
947173273Ssam
948173273Ssam	/* Try to defer to the bhndb bus parent */
949173273Ssam	if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core))
950173273Ssam		return (true);
951173273Ssam
952173273Ssam	/* Otherwise, we treat bridge-capable cores as unpopulated if they're
953178354Ssam	 * not the configured host bridge */
954173273Ssam	if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core)))
955173273Ssam		return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child);
956173273Ssam
957173273Ssam	/* Otherwise, assume the core is populated */
958173273Ssam	return (false);
959173273Ssam}
960173273Ssam
961173273Ssam/* ascending core index comparison used by bhndb_find_hostb_device() */
962173273Ssamstatic int
963173273Ssamcompare_core_index(const void *lhs, const void *rhs)
964173273Ssam{
965173273Ssam	u_int left = bhnd_get_core_index(*(const device_t *) lhs);
966173273Ssam	u_int right = bhnd_get_core_index(*(const device_t *) rhs);
967173273Ssam
968173273Ssam	if (left < right)
969173273Ssam		return (-1);
970173273Ssam	else if (left > right)
971173273Ssam		return (1);
972173273Ssam	else
973173273Ssam		return (0);
974173273Ssam}
975173273Ssam
976173273Ssam/**
977173273Ssam * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE().
978178354Ssam *
979178354Ssam * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged
980178354Ssam * bhnd(4) devices to determine the hostb core:
981178354Ssam *
982178354Ssam * - The core must have a Broadcom vendor ID.
983178354Ssam * - The core devclass must match the bridge type.
984178354Ssam * - The core must be the first device on the bus with the bridged device
985178354Ssam *   class.
986178354Ssam *
987178354Ssam * @param dev The bhndb device
988178354Ssam * @param child The requesting bhnd bus.
989178354Ssam */
990178354Ssamstatic device_t
991178354Ssambhndb_find_hostb_device(device_t dev, device_t child)
992178354Ssam{
993178354Ssam	struct bhndb_softc		*sc;
994178354Ssam	struct bhnd_device_match	 md;
995178354Ssam	device_t			 hostb_dev, *devlist;
996178354Ssam	int				 devcnt, error;
997178354Ssam
998178354Ssam	sc = device_get_softc(dev);
999178354Ssam
1000178354Ssam	/* Set up a match descriptor for the required device class. */
1001178354Ssam	md = (struct bhnd_device_match) {
1002173273Ssam		BHND_MATCH_CORE_CLASS(sc->bridge_class),
1003173273Ssam		BHND_MATCH_CORE_UNIT(0)
1004173273Ssam	};
1005173273Ssam
1006173273Ssam	/* Must be the absolute first matching device on the bus. */
1007173273Ssam	if ((error = device_get_children(child, &devlist, &devcnt)))
1008173273Ssam		return (false);
1009173273Ssam
1010173273Ssam	/* Sort by core index value, ascending */
1011173273Ssam	qsort(devlist, devcnt, sizeof(*devlist), compare_core_index);
1012173273Ssam
1013178354Ssam	/* Find the hostb device */
1014178354Ssam	hostb_dev = NULL;
1015178354Ssam	for (int i = 0; i < devcnt; i++) {
1016178354Ssam		if (bhnd_device_matches(devlist[i], &md)) {
1017173273Ssam			hostb_dev = devlist[i];
1018178354Ssam			break;
1019178354Ssam		}
1020178354Ssam	}
1021173273Ssam
1022173273Ssam	/* Clean up */
1023173273Ssam	free(devlist, M_TEMP);
1024173273Ssam
1025173273Ssam	return (hostb_dev);
1026173273Ssam}
1027173273Ssam
1028173273Ssam/**
1029173273Ssam * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE().
1030173273Ssam */
1031173273Ssamstatic struct resource *
1032173273Ssambhndb_alloc_resource(device_t dev, device_t child, int type,
1033173273Ssam    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
1034173273Ssam{
1035173273Ssam	struct bhndb_softc		*sc;
1036173273Ssam	struct resource_list_entry	*rle;
1037173273Ssam	struct resource			*rv;
1038173273Ssam	struct rman			*rm;
1039173273Ssam	int				 error;
1040173273Ssam	bool				 passthrough, isdefault;
1041173273Ssam
1042173273Ssam	sc = device_get_softc(dev);
1043173273Ssam	passthrough = (device_get_parent(child) != dev);
1044173273Ssam	isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
1045173273Ssam	rle = NULL;
1046173273Ssam
1047173273Ssam	/* Populate defaults */
1048173273Ssam	if (!passthrough && isdefault) {
1049173273Ssam		/* Fetch the resource list entry. */
1050173273Ssam		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
1051173273Ssam		    type, *rid);
1052173273Ssam		if (rle == NULL) {
1053173273Ssam			device_printf(dev,
1054173273Ssam			    "default resource %#x type %d for child %s "
1055173273Ssam			    "not found\n", *rid, type,
1056173273Ssam			    device_get_nameunit(child));
1057173273Ssam
1058173273Ssam			return (NULL);
1059173273Ssam		}
1060173273Ssam
1061173273Ssam		if (rle->res != NULL) {
1062173273Ssam			device_printf(dev,
1063173273Ssam			    "resource entry %#x type %d for child %s is busy\n",
1064173273Ssam			    *rid, type, device_get_nameunit(child));
1065173273Ssam
1066173273Ssam			return (NULL);
1067173273Ssam		}
1068173273Ssam
1069173273Ssam		start = rle->start;
1070178354Ssam		end = rle->end;
1071178354Ssam		count = ulmax(count, rle->count);
1072178354Ssam	}
1073178354Ssam
1074178354Ssam	/* Validate resource addresses */
1075178354Ssam	if (start > end || count > ((end - start) + 1))
1076178354Ssam		return (NULL);
1077178354Ssam
1078178354Ssam	/* Fetch the resource manager */
1079173273Ssam	rm = bhndb_get_rman(sc, child, type);
1080173273Ssam	if (rm == NULL)
1081178354Ssam		return (NULL);
1082173273Ssam
1083178354Ssam	/* Make our reservation */
1084178354Ssam	rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
1085178354Ssam	    child);
1086178354Ssam	if (rv == NULL)
1087178354Ssam		return (NULL);
1088178354Ssam
1089178354Ssam	rman_set_rid(rv, *rid);
1090181197Ssam
1091178354Ssam	/* Activate */
1092178354Ssam	if (flags & RF_ACTIVE) {
1093178354Ssam		error = bus_activate_resource(child, type, *rid, rv);
1094178354Ssam		if (error) {
1095178354Ssam			device_printf(dev,
1096178354Ssam			    "failed to activate entry %#x type %d for "
1097178354Ssam				"child %s: %d\n",
1098178354Ssam			     *rid, type, device_get_nameunit(child), error);
1099178354Ssam
1100181197Ssam			rman_release_resource(rv);
1101178354Ssam
1102173273Ssam			return (NULL);
1103173273Ssam		}
1104173273Ssam	}
1105173273Ssam
1106173273Ssam	/* Update child's resource list entry */
1107173273Ssam	if (rle != NULL) {
1108173273Ssam		rle->res = rv;
1109173273Ssam		rle->start = rman_get_start(rv);
1110173273Ssam		rle->end = rman_get_end(rv);
1111173273Ssam		rle->count = rman_get_size(rv);
1112173273Ssam	}
1113173273Ssam
1114173273Ssam	return (rv);
1115173273Ssam}
1116173273Ssam
1117173273Ssam/**
1118173273Ssam * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE().
1119173273Ssam */
1120178354Ssamstatic int
1121173273Ssambhndb_release_resource(device_t dev, device_t child, int type, int rid,
1122173273Ssam    struct resource *r)
1123173273Ssam{
1124173273Ssam	int error;
1125173273Ssam
1126173273Ssam	/* Deactivate resources */
1127173273Ssam	if (rman_get_flags(r) & RF_ACTIVE) {
1128170530Ssam		error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
1129170530Ssam		if (error)
1130170530Ssam			return (error);
1131170530Ssam	}
1132170530Ssam
1133170530Ssam	if ((error = rman_release_resource(r)))
1134170530Ssam		return (error);
1135170530Ssam
1136170530Ssam	return (0);
1137170530Ssam}
1138170530Ssam
1139170530Ssam/**
1140178354Ssam * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE().
1141170530Ssam */
1142170530Ssamstatic int
1143170530Ssambhndb_adjust_resource(device_t dev, device_t child, int type,
1144170530Ssam    struct resource *r, rman_res_t start, rman_res_t end)
1145170530Ssam{
1146170530Ssam	struct bhndb_softc		*sc;
1147170530Ssam	struct rman			*rm;
1148170530Ssam	rman_res_t			 mstart, mend;
1149170530Ssam	int				 error;
1150170530Ssam
1151170530Ssam	sc = device_get_softc(dev);
1152170530Ssam	error = 0;
1153170530Ssam
1154170530Ssam	/* Verify basic constraints */
1155170530Ssam	if (end <= start)
1156173273Ssam		return (EINVAL);
1157173273Ssam
1158178354Ssam	/* Fetch resource manager */
1159170530Ssam	rm = bhndb_get_rman(sc, child, type);
1160170530Ssam	if (rm == NULL)
1161170530Ssam		return (ENXIO);
1162173273Ssam
1163173273Ssam	if (!rman_is_region_manager(r, rm))
1164173273Ssam		return (ENXIO);
1165173273Ssam
1166173273Ssam	BHNDB_LOCK(sc);
1167173273Ssam
1168170530Ssam	/* If not active, allow any range permitted by the resource manager */
1169170530Ssam	if (!(rman_get_flags(r) & RF_ACTIVE))
1170170530Ssam		goto done;
1171170530Ssam
1172173273Ssam	/* Otherwise, the range is limited to the existing register window
1173178354Ssam	 * mapping */
1174170530Ssam	error = bhndb_find_resource_limits(sc->bus_res, r, &mstart, &mend);
1175173273Ssam	if (error)
1176170530Ssam		goto done;
1177173273Ssam
1178170530Ssam	if (start < mstart || end > mend) {
1179170530Ssam		error = EINVAL;
1180170530Ssam		goto done;
1181170530Ssam	}
1182170530Ssam
1183170530Ssam	/* Fall through */
1184172055Ssamdone:
1185170530Ssam	if (!error)
1186170530Ssam		error = rman_adjust_resource(r, start, end);
1187170530Ssam
1188173273Ssam	BHNDB_UNLOCK(sc);
1189173273Ssam	return (error);
1190173273Ssam}
1191173273Ssam
1192173273Ssam/**
1193178354Ssam * Initialize child resource @p r with a virtual address, tag, and handle
1194178354Ssam * copied from @p parent, adjusted to contain only the range defined by
1195173273Ssam * @p offsize and @p size.
1196173273Ssam *
1197178354Ssam * @param r The register to be initialized.
1198173273Ssam * @param parent The parent bus resource that fully contains the subregion.
1199173273Ssam * @param offset The subregion offset within @p parent.
1200173273Ssam * @param size The subregion size.
1201173273Ssam * @p r.
1202170530Ssam */
1203173273Ssamstatic int
1204173273Ssambhndb_init_child_resource(struct resource *r,
1205173273Ssam    struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
1206178354Ssam{
1207173273Ssam	bus_space_handle_t	bh, child_bh;
1208173273Ssam	bus_space_tag_t		bt;
1209173273Ssam	uintptr_t		vaddr;
1210173273Ssam	int			error;
1211173273Ssam
1212178354Ssam	/* Fetch the parent resource's real bus values */
1213173273Ssam	vaddr = (uintptr_t) rman_get_virtual(parent);
1214173273Ssam	bt = rman_get_bustag(parent);
1215173273Ssam	bh = rman_get_bushandle(parent);
1216173273Ssam
1217173273Ssam	/* Configure child resource with window-adjusted real bus values */
1218173273Ssam	vaddr += offset;
1219178354Ssam	error = bus_space_subregion(bt, bh, offset, size, &child_bh);
1220173273Ssam	if (error)
1221173273Ssam		return (error);
1222173273Ssam
1223173273Ssam	rman_set_virtual(r, (void *) vaddr);
1224173273Ssam	rman_set_bustag(r, bt);
1225173273Ssam	rman_set_bushandle(r, child_bh);
1226173273Ssam
1227173273Ssam	return (0);
1228173273Ssam}
1229173273Ssam
1230170530Ssam/**
1231170530Ssam * Attempt activation of a fixed register window mapping for @p child.
1232170530Ssam *
1233170530Ssam * @param sc BHNDB device state.
1234170530Ssam * @param region The static region definition capable of mapping @p r.
1235170530Ssam * @param child A child requesting resource activation.
1236170530Ssam * @param type Resource type.
1237170530Ssam * @param rid Resource identifier.
1238178354Ssam * @param r Resource to be activated.
1239170530Ssam *
1240170530Ssam * @retval 0 if @p r was activated successfully
1241170530Ssam * @retval ENOENT if no fixed register window was found.
1242170530Ssam * @retval non-zero if @p r could not be activated.
1243170530Ssam */
1244170530Ssamstatic int
1245170530Ssambhndb_activate_static_region(struct bhndb_softc *sc,
1246170530Ssam    struct bhndb_region *region, device_t child, int type, int rid,
1247170530Ssam    struct resource *r)
1248170530Ssam{
1249170530Ssam	struct resource			*bridge_res;
1250170530Ssam	const struct bhndb_regwin	*win;
1251170530Ssam	bhnd_size_t			 parent_offset;
1252170530Ssam	rman_res_t			 r_start, r_size;
1253178354Ssam	int				 error;
1254170530Ssam
1255170530Ssam	win = region->static_regwin;
1256170530Ssam
1257178354Ssam	KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type),
1258170530Ssam	    ("can't activate non-static region"));
1259170530Ssam
1260170530Ssam	r_start = rman_get_start(r);
1261170530Ssam	r_size = rman_get_size(r);
1262170530Ssam
1263170530Ssam	/* Find the corresponding bridge resource */
1264170530Ssam	bridge_res = bhndb_find_regwin_resource(sc->bus_res, win);
1265170530Ssam	if (bridge_res == NULL)
1266170530Ssam		return (ENXIO);
1267170530Ssam
1268170530Ssam	/* Calculate subregion offset within the parent resource */
1269170530Ssam	parent_offset = r_start - region->addr;
1270170530Ssam	parent_offset += win->win_offset;
1271170530Ssam
1272170530Ssam	/* Configure resource with its real bus values. */
1273170530Ssam	error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size);
1274170530Ssam	if (error)
1275170530Ssam		return (error);
1276170530Ssam
1277170530Ssam	/* Mark active */
1278170530Ssam	if ((error = rman_activate_resource(r)))
1279170530Ssam		return (error);
1280170530Ssam
1281170530Ssam	return (0);
1282178354Ssam}
1283170530Ssam
1284170530Ssam/**
1285170530Ssam * Attempt to allocate/retain a dynamic register window for @p r, returning
1286170530Ssam * the retained window.
1287170530Ssam *
1288170530Ssam * @param sc The bhndb driver state.
1289170530Ssam * @param r The resource for which a window will be retained.
1290170530Ssam */
1291170530Ssamstatic struct bhndb_dw_alloc *
1292170530Ssambhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r)
1293170530Ssam{
1294170530Ssam	struct bhndb_dw_alloc	*dwa;
1295170530Ssam	rman_res_t		 r_start, r_size;
1296170530Ssam	int			 error;
1297170530Ssam
1298170530Ssam	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1299170530Ssam
1300170530Ssam	r_start = rman_get_start(r);
1301170530Ssam	r_size = rman_get_size(r);
1302170530Ssam
1303170530Ssam	/* Look for an existing dynamic window we can reference */
1304170530Ssam	dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size);
1305170530Ssam	if (dwa != NULL) {
1306170530Ssam		if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0)
1307170530Ssam			return (dwa);
1308170530Ssam
1309170530Ssam		return (NULL);
1310178354Ssam	}
1311170530Ssam
1312170530Ssam	/* Otherwise, try to reserve a free window */
1313178354Ssam	dwa = bhndb_dw_next_free(sc->bus_res);
1314170530Ssam	if (dwa == NULL) {
1315170530Ssam		/* No free windows */
1316170530Ssam		return (NULL);
1317170530Ssam	}
1318170530Ssam
1319170530Ssam	/* Set the window target */
1320170530Ssam	error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r),
1321170530Ssam	    rman_get_size(r));
1322170530Ssam	if (error) {
1323170530Ssam		device_printf(sc->dev, "dynamic window initialization "
1324170530Ssam			"for 0x%llx-0x%llx failed: %d\n",
1325170530Ssam			(unsigned long long) r_start,
1326170530Ssam			(unsigned long long) r_start + r_size - 1,
1327170530Ssam			error);
1328170530Ssam		return (NULL);
1329170530Ssam	}
1330170530Ssam
1331170530Ssam	/* Add our reservation */
1332170530Ssam	if (bhndb_dw_retain(sc->bus_res, dwa, r))
1333170530Ssam		return (NULL);
1334170530Ssam
1335170530Ssam	return (dwa);
1336170530Ssam}
1337170530Ssam
1338170530Ssam/**
1339170530Ssam * Activate a resource using any viable static or dynamic register window.
1340170530Ssam *
1341170530Ssam * @param sc The bhndb driver state.
1342170530Ssam * @param child The child holding ownership of @p r.
1343170530Ssam * @param type The type of the resource to be activated.
1344170530Ssam * @param rid The resource ID of @p r.
1345170530Ssam * @param r The resource to be activated
1346170530Ssam * @param[out] indirect On error and if not NULL, will be set to 'true' if
1347170530Ssam * the caller should instead use an indirect resource mapping.
1348170530Ssam *
1349170530Ssam * @retval 0 success
1350170530Ssam * @retval non-zero activation failed.
1351170530Ssam */
1352170530Ssamstatic int
1353170530Ssambhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
1354170530Ssam    int rid, struct resource *r, bool *indirect)
1355170530Ssam{
1356170530Ssam	struct bhndb_region	*region;
1357170530Ssam	struct bhndb_dw_alloc	*dwa;
1358170530Ssam	bhndb_priority_t	 dw_priority;
1359170530Ssam	rman_res_t		 r_start, r_size;
1360170530Ssam	rman_res_t		 parent_offset;
1361170530Ssam	int			 error;
1362170530Ssam
1363170530Ssam	BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
1364170530Ssam
1365170530Ssam	// TODO - IRQs
1366170530Ssam	if (type != SYS_RES_MEMORY)
1367170530Ssam		return (ENXIO);
1368170530Ssam
1369173273Ssam	if (indirect)
1370173273Ssam		*indirect = false;
1371173273Ssam
1372170530Ssam	r_start = rman_get_start(r);
1373170530Ssam	r_size = rman_get_size(r);
1374170530Ssam
1375170530Ssam	/* Activate native addrspace resources using the host address space */
1376170530Ssam	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) {
1377170530Ssam		struct resource *parent;
1378170530Ssam
1379170530Ssam		/* Find the bridge resource referenced by the child */
1380170530Ssam		parent = bhndb_find_resource_range(sc->bus_res, r_start,
1381170530Ssam		    r_size);
1382170530Ssam		if (parent == NULL) {
1383170530Ssam			device_printf(sc->dev, "host resource not found "
1384170530Ssam			     "for 0x%llx-0x%llx\n",
1385170530Ssam			     (unsigned long long) r_start,
1386170530Ssam			     (unsigned long long) r_start + r_size - 1);
1387170530Ssam			return (ENOENT);
1388170530Ssam		}
1389170530Ssam
1390170530Ssam		/* Initialize child resource with the real bus values */
1391170530Ssam		error = bhndb_init_child_resource(r, parent,
1392170530Ssam		    r_start - rman_get_start(parent), r_size);
1393170530Ssam		if (error)
1394170530Ssam			return (error);
1395170530Ssam
1396170530Ssam		/* Try to activate child resource */
1397170530Ssam		return (rman_activate_resource(r));
1398170530Ssam	}
1399170530Ssam
1400170530Ssam	/* Default to low priority */
1401170530Ssam	dw_priority = BHNDB_PRIORITY_LOW;
1402178354Ssam
1403170530Ssam	/* Look for a bus region matching the resource's address range */
1404170530Ssam	region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
1405170530Ssam	if (region != NULL)
1406170530Ssam		dw_priority = region->priority;
1407170530Ssam
1408170530Ssam	/* Prefer static mappings over consuming a dynamic windows. */
1409170530Ssam	if (region && region->static_regwin) {
1410170530Ssam		error = bhndb_activate_static_region(sc, region, child, type,
1411170530Ssam		    rid, r);
1412170530Ssam		if (error)
1413170530Ssam			device_printf(sc->dev, "static window allocation "
1414170530Ssam			     "for 0x%llx-0x%llx failed\n",
1415170530Ssam			     (unsigned long long) r_start,
1416170530Ssam			     (unsigned long long) r_start + r_size - 1);
1417170530Ssam		return (error);
1418170530Ssam	}
1419170530Ssam
1420170530Ssam	/* A dynamic window will be required; is this resource high enough
1421170530Ssam	 * priority to be reserved a dynamic window? */
1422170530Ssam	if (dw_priority < sc->bus_res->min_prio) {
1423170530Ssam		if (indirect)
1424178354Ssam			*indirect = true;
1425170530Ssam
1426170530Ssam		return (ENOMEM);
1427170530Ssam	}
1428173273Ssam
1429173273Ssam	/* Find and retain a usable window */
1430173273Ssam	BHNDB_LOCK(sc); {
1431173273Ssam		dwa = bhndb_retain_dynamic_window(sc, r);
1432170530Ssam	} BHNDB_UNLOCK(sc);
1433170530Ssam
1434170530Ssam	if (dwa == NULL) {
1435170530Ssam		if (indirect)
1436170530Ssam			*indirect = true;
1437173273Ssam		return (ENOMEM);
1438173273Ssam	}
1439173273Ssam
1440173273Ssam	/* Configure resource with its real bus values. */
1441173273Ssam	parent_offset = dwa->win->win_offset;
1442173273Ssam	parent_offset += r_start - dwa->target;
1443178354Ssam
1444170530Ssam	error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset,
1445170530Ssam	    dwa->win->win_size);
1446170530Ssam	if (error)
1447170530Ssam		goto failed;
1448173273Ssam
1449178354Ssam	/* Mark active */
1450173273Ssam	if ((error = rman_activate_resource(r)))
1451173273Ssam		goto failed;
1452173273Ssam
1453173273Ssam	return (0);
1454173273Ssam
1455178354Ssamfailed:
1456170530Ssam	/* Release our region allocation. */
1457173273Ssam	BHNDB_LOCK(sc);
1458170530Ssam	bhndb_dw_release(sc->bus_res, dwa, r);
1459170530Ssam	BHNDB_UNLOCK(sc);
1460170530Ssam
1461170530Ssam	return (error);
1462170530Ssam}
1463170530Ssam
1464170530Ssam/**
1465170530Ssam * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE().
1466170530Ssam *
1467170530Ssam * Maps resource activation requests to a viable static or dynamic
1468170530Ssam * register window, if any.
1469170530Ssam */
1470170530Ssamstatic int
1471170530Ssambhndb_activate_resource(device_t dev, device_t child, int type, int rid,
1472170530Ssam    struct resource *r)
1473170530Ssam{
1474170530Ssam	struct bhndb_softc *sc = device_get_softc(dev);
1475170530Ssam
1476173273Ssam	return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL));
1477173273Ssam}
1478175877Ssam
1479178354Ssam/**
1480173273Ssam * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE().
1481173273Ssam */
1482173273Ssamstatic int
1483173273Ssambhndb_deactivate_resource(device_t dev, device_t child, int type,
1484178354Ssam    int rid, struct resource *r)
1485173273Ssam{
1486173273Ssam	struct bhndb_dw_alloc	*dwa;
1487173273Ssam	struct bhndb_softc	*sc;
1488178354Ssam	struct rman		*rm;
1489173273Ssam	int			 error;
1490173273Ssam
1491173273Ssam	sc = device_get_softc(dev);
1492173273Ssam
1493173273Ssam	if ((rm = bhndb_get_rman(sc, child, type)) == NULL)
1494178354Ssam		return (EINVAL);
1495173273Ssam
1496173273Ssam	/* Mark inactive */
1497173273Ssam	if ((error = rman_deactivate_resource(r)))
1498178354Ssam		return (error);
1499170530Ssam
1500170530Ssam	/* Free any dynamic window allocation. */
1501170530Ssam	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1502170530Ssam		BHNDB_LOCK(sc);
1503170530Ssam		dwa = bhndb_dw_find_resource(sc->bus_res, r);
1504170530Ssam		if (dwa != NULL)
1505170530Ssam			bhndb_dw_release(sc->bus_res, dwa, r);
1506170530Ssam		BHNDB_UNLOCK(sc);
1507170530Ssam	}
1508170530Ssam
1509170530Ssam	return (0);
1510170530Ssam}
1511170530Ssam
1512170530Ssam/**
1513170530Ssam * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST().
1514178354Ssam */
1515170530Ssamstatic struct resource_list *
1516170530Ssambhndb_get_resource_list(device_t dev, device_t child)
1517170530Ssam{
1518170530Ssam	struct bhndb_devinfo *dinfo = device_get_ivars(child);
1519170530Ssam	return (&dinfo->resources);
1520170530Ssam}
1521170530Ssam
1522170530Ssam/**
1523170530Ssam * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE().
1524170530Ssam *
1525170530Ssam * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to
1526170530Ssam * be activated by the bridge.
1527170530Ssam *
1528170530Ssam * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register
1529170530Ssam * window, a dynamic register window, or configures @p r as an indirect
1530170530Ssam * resource -- in that order.
1531170530Ssam */
1532173273Ssamstatic int
1533170530Ssambhndb_activate_bhnd_resource(device_t dev, device_t child,
1534170530Ssam    int type, int rid, struct bhnd_resource *r)
1535170530Ssam{
1536170530Ssam	struct bhndb_softc	*sc;
1537170530Ssam	struct bhndb_region	*region;
1538170530Ssam	rman_res_t		 r_start, r_size;
1539170530Ssam	int 			 error;
1540170530Ssam	bool			 indirect;
1541170530Ssam
1542170530Ssam	KASSERT(!r->direct,
1543170530Ssam	    ("direct flag set on inactive resource"));
1544170530Ssam
1545178354Ssam	KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE),
1546170530Ssam	    ("RF_ACTIVE set on inactive resource"));
1547170530Ssam
1548170530Ssam	sc = device_get_softc(dev);
1549170530Ssam
1550170530Ssam	r_start = rman_get_start(r->res);
1551170530Ssam	r_size = rman_get_size(r->res);
1552178354Ssam
1553170530Ssam	/* Verify bridged address range's resource priority, and skip direct
1554170530Ssam	 * allocation if the priority is too low. */
1555170530Ssam	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1556178354Ssam		bhndb_priority_t r_prio;
1557170530Ssam
1558170530Ssam		region = bhndb_find_resource_region(sc->bus_res, r_start,
1559170530Ssam		    r_size);
1560170530Ssam		if (region != NULL)
1561170530Ssam			r_prio = region->priority;
1562170530Ssam		else
1563170530Ssam			r_prio = BHNDB_PRIORITY_NONE;
1564170530Ssam
1565170530Ssam		/* If less than the minimum dynamic window priority, this
1566178354Ssam		 * resource should always be indirect. */
1567170530Ssam		if (r_prio < sc->bus_res->min_prio)
1568173273Ssam			return (0);
1569170530Ssam	}
1570170530Ssam
1571170530Ssam	/* Attempt direct activation */
1572170530Ssam	error = bhndb_try_activate_resource(sc, child, type, rid, r->res,
1573173273Ssam	    &indirect);
1574178354Ssam	if (!error) {
1575173273Ssam		r->direct = true;
1576173273Ssam	} else if (indirect) {
1577173273Ssam		/* The request was valid, but no viable register window is
1578170530Ssam		 * available; indirection must be employed. */
1579178354Ssam		error = 0;
1580170530Ssam		r->direct = false;
1581170530Ssam	}
1582170530Ssam
1583178354Ssam	if (BHNDB_DEBUG(PRIO) &&
1584170530Ssam	    bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED)
1585170530Ssam	{
1586170530Ssam		device_printf(child, "activated 0x%llx-0x%llx as %s "
1587170530Ssam		    "resource\n",
1588178354Ssam		    (unsigned long long) r_start,
1589170530Ssam		    (unsigned long long) r_start + r_size - 1,
1590170530Ssam		    r->direct ? "direct" : "indirect");
1591170530Ssam	}
1592178354Ssam
1593170530Ssam	return (error);
1594170530Ssam};
1595170530Ssam
1596170530Ssam/**
1597170530Ssam * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE().
1598170530Ssam */
1599170530Ssamstatic int
1600170530Ssambhndb_deactivate_bhnd_resource(device_t dev, device_t child,
1601170530Ssam    int type, int rid, struct bhnd_resource *r)
1602178354Ssam{
1603178354Ssam	int error;
1604178354Ssam
1605178354Ssam	/* Indirect resources don't require activation */
1606178354Ssam	if (!r->direct)
1607178354Ssam		return (0);
1608178354Ssam
1609178354Ssam	KASSERT(rman_get_flags(r->res) & RF_ACTIVE,
1610178354Ssam	    ("RF_ACTIVE not set on direct resource"));
1611178354Ssam
1612178354Ssam	/* Perform deactivation */
1613178354Ssam	error = bus_deactivate_resource(child, type, rid, r->res);
1614178354Ssam	if (!error)
1615178354Ssam		r->direct = false;
1616178354Ssam
1617178354Ssam	return (error);
1618178354Ssam};
1619178354Ssam
1620178354Ssam/**
1621178354Ssam * Slow path for bhndb_io_resource().
1622178354Ssam *
1623178354Ssam * Iterates over the existing allocated dynamic windows looking for a viable
1624178354Ssam * in-use region; the first matching region is returned.
1625178354Ssam */
1626178354Ssamstatic struct bhndb_dw_alloc *
1627178354Ssambhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
1628178354Ssam    bus_size_t size, bus_size_t *offset)
1629178354Ssam{
1630178354Ssam	struct bhndb_resources	*br;
1631178354Ssam	struct bhndb_dw_alloc	*dwa;
1632178354Ssam
1633178354Ssam	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1634178354Ssam
1635170530Ssam	br = sc->bus_res;
1636170530Ssam
1637170530Ssam	/* Search for an existing dynamic mapping of this address range.
1638170530Ssam	 * Static regions are not searched, as a statically mapped
1639170530Ssam	 * region would never be allocated as an indirect resource. */
1640170530Ssam	for (size_t i = 0; i < br->dwa_count; i++) {
1641170530Ssam		const struct bhndb_regwin *win;
1642170530Ssam
1643170530Ssam		dwa = &br->dw_alloc[i];
1644170530Ssam		win = dwa->win;
1645170530Ssam
1646170530Ssam		KASSERT(win->win_type == BHNDB_REGWIN_T_DYN,
1647170530Ssam			("invalid register window type"));
1648170530Ssam
1649170530Ssam		/* Verify the range */
1650170530Ssam		if (addr < dwa->target)
1651170530Ssam			continue;
1652170530Ssam
1653170530Ssam		if (addr + size > dwa->target + win->win_size)
1654173273Ssam			continue;
1655173273Ssam
1656173273Ssam		/* Found */
1657170530Ssam		*offset = dwa->win->win_offset;
1658170530Ssam		*offset += addr - dwa->target;
1659170530Ssam
1660170530Ssam		return (dwa);
1661170530Ssam	}
1662170530Ssam
1663170530Ssam	/* not found */
1664170530Ssam	return (NULL);
1665170530Ssam}
1666170530Ssam
1667170530Ssam/**
1668170530Ssam * Find the bridge resource to be used for I/O requests.
1669178354Ssam *
1670173273Ssam * @param sc Bridge driver state.
1671173273Ssam * @param addr The I/O target address.
1672173273Ssam * @param size The size of the I/O operation to be performed at @p addr.
1673178354Ssam * @param[out] offset The offset within the returned resource at which
1674178354Ssam * to perform the I/O request.
1675178354Ssam */
1676178354Ssamstatic inline struct bhndb_dw_alloc *
1677170530Ssambhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
1678170530Ssam    bus_size_t *offset)
1679170530Ssam{
1680178953Ssam	struct bhndb_resources	*br;
1681178953Ssam	struct bhndb_dw_alloc	*dwa;
1682178953Ssam	int			 error;
1683178953Ssam
1684170530Ssam	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1685170530Ssam
1686170530Ssam	br = sc->bus_res;
1687170530Ssam
1688170530Ssam	/* Try to fetch a free window */
1689173273Ssam	dwa = bhndb_dw_next_free(br);
1690173273Ssam
1691173273Ssam	/*
1692173273Ssam	 * If no dynamic windows are available, look for an existing
1693173273Ssam	 * region that maps the target range.
1694173273Ssam	 *
1695173273Ssam	 * If none are found, this is a child driver bug -- our window
1696178354Ssam	 * over-commit should only fail in the case where a child driver leaks
1697173273Ssam	 * resources, or perform operations out-of-order.
1698173273Ssam	 *
1699173273Ssam	 * Broadcom HND chipsets are designed to not require register window
1700173273Ssam	 * swapping during execution; as long as the child devices are
1701178354Ssam	 * attached/detached correctly, using the hardware's required order
1702173273Ssam	 * of operations, there should always be a window available for the
1703178354Ssam	 * current operation.
1704173273Ssam	 */
1705173273Ssam	if (dwa == NULL) {
1706173273Ssam		dwa = bhndb_io_resource_slow(sc, addr, size, offset);
1707173273Ssam		if (dwa == NULL) {
1708173273Ssam			panic("register windows exhausted attempting to map "
1709173273Ssam			    "0x%llx-0x%llx\n",
1710173273Ssam			    (unsigned long long) addr,
1711173273Ssam			    (unsigned long long) addr+size-1);
1712178354Ssam		}
1713173273Ssam
1714173273Ssam		return (dwa);
1715178354Ssam	}
1716173273Ssam
1717173273Ssam	/* Adjust the window if the I/O request won't fit in the current
1718173273Ssam	 * target range. */
1719173273Ssam	if (addr < dwa->target ||
1720170530Ssam	   (dwa->target + dwa->win->win_size) - addr < size)
1721170530Ssam	{
1722170530Ssam		error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr,
1723170530Ssam		    size);
1724170530Ssam		if (error) {
1725170530Ssam		    panic("failed to set register window target mapping "
1726170530Ssam			    "0x%llx-0x%llx\n",
1727170530Ssam			    (unsigned long long) addr,
1728178354Ssam			    (unsigned long long) addr+size-1);
1729170530Ssam		}
1730170530Ssam	}
1731170530Ssam
1732170530Ssam	/* Calculate the offset and return */
1733170530Ssam	*offset = (addr - dwa->target) + dwa->win->win_offset;
1734178354Ssam	return (dwa);
1735170530Ssam}
1736170530Ssam
1737170530Ssam/*
1738170530Ssam * BHND_BUS_(READ|WRITE_* implementations
1739170530Ssam */
1740170530Ssam
1741170530Ssam/* bhndb_bus_(read|write) common implementation */
1742170530Ssam#define	BHNDB_IO_COMMON_SETUP(_io_size)				\
1743170530Ssam	struct bhndb_softc	*sc;				\
1744170530Ssam	struct bhndb_dw_alloc	*dwa;				\
1745170530Ssam	struct resource		*io_res;			\
1746170530Ssam	bus_size_t		 io_offset;			\
1747170530Ssam								\
1748170530Ssam	sc = device_get_softc(dev);				\
1749170530Ssam								\
1750170530Ssam	BHNDB_LOCK(sc);						\
1751170530Ssam	dwa = bhndb_io_resource(sc, rman_get_start(r->res) +	\
1752170530Ssam	    offset, _io_size, &io_offset);			\
1753170530Ssam	io_res = dwa->parent_res;				\
1754170530Ssam								\
1755170530Ssam	KASSERT(!r->direct,					\
1756178354Ssam	    ("bhnd_bus slow path used for direct resource"));	\
1757170530Ssam								\
1758170530Ssam	KASSERT(rman_get_flags(io_res) & RF_ACTIVE,		\
1759170530Ssam	    ("i/o resource is not active"));
1760170530Ssam
1761170530Ssam#define	BHNDB_IO_COMMON_TEARDOWN()				\
1762170530Ssam	BHNDB_UNLOCK(sc);
1763170530Ssam
1764170530Ssam/* Defines a bhndb_bus_read_* method implementation */
1765170530Ssam#define	BHNDB_IO_READ(_type, _name)				\
1766170530Ssamstatic _type							\
1767170530Ssambhndb_bus_read_ ## _name (device_t dev, device_t child,		\
1768170530Ssam    struct bhnd_resource *r, bus_size_t offset)			\
1769170530Ssam{								\
1770170530Ssam	_type v;						\
1771170530Ssam	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1772178354Ssam	v = bus_read_ ## _name (io_res, io_offset);		\
1773178354Ssam	BHNDB_IO_COMMON_TEARDOWN();				\
1774170530Ssam								\
1775170530Ssam	return (v);						\
1776178354Ssam}
1777173273Ssam
1778173273Ssam/* Defines a bhndb_bus_write_* method implementation */
1779170530Ssam#define	BHNDB_IO_WRITE(_type, _name)				\
1780178354Ssamstatic void							\
1781170530Ssambhndb_bus_write_ ## _name (device_t dev, device_t child,	\
1782170530Ssam    struct bhnd_resource *r, bus_size_t offset, _type value)	\
1783170530Ssam{								\
1784170530Ssam	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1785170530Ssam	bus_write_ ## _name (io_res, io_offset, value);		\
1786170530Ssam	BHNDB_IO_COMMON_TEARDOWN();				\
1787170530Ssam}
1788170530Ssam
1789170530Ssam/* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */
1790170530Ssam#define	BHNDB_IO_MISC(_type, _ptr, _op, _size)			\
1791170530Ssamstatic void							\
1792170530Ssambhndb_bus_ ## _op ## _ ## _size (device_t dev,			\
1793170530Ssam    device_t child, struct bhnd_resource *r, bus_size_t offset,	\
1794170530Ssam    _type _ptr datap, bus_size_t count)				\
1795170530Ssam{								\
1796170530Ssam	BHNDB_IO_COMMON_SETUP(sizeof(_type) * count);		\
1797178354Ssam	bus_ ## _op ## _ ## _size (io_res, io_offset,		\
1798170530Ssam	    datap, count);					\
1799170530Ssam	BHNDB_IO_COMMON_TEARDOWN();				\
1800170530Ssam}
1801170530Ssam
1802170530Ssam/* Defines a complete set of read/write methods */
1803178354Ssam#define	BHNDB_IO_METHODS(_type, _size)				\
1804170530Ssam	BHNDB_IO_READ(_type, _size)				\
1805170530Ssam	BHNDB_IO_WRITE(_type, _size)				\
1806170530Ssam								\
1807170530Ssam	BHNDB_IO_READ(_type, stream_ ## _size)			\
1808170530Ssam	BHNDB_IO_WRITE(_type, stream_ ## _size)			\
1809170530Ssam								\
1810170530Ssam	BHNDB_IO_MISC(_type, *, read_multi, _size)		\
1811170530Ssam	BHNDB_IO_MISC(_type, *, write_multi, _size)		\
1812170530Ssam								\
1813170530Ssam	BHNDB_IO_MISC(_type, *, read_multi_stream, _size)	\
1814170530Ssam	BHNDB_IO_MISC(_type, *, write_multi_stream, _size)	\
1815170530Ssam								\
1816170530Ssam	BHNDB_IO_MISC(_type,  , set_multi, _size)		\
1817178354Ssam	BHNDB_IO_MISC(_type,  , set_region, _size)		\
1818170530Ssam	BHNDB_IO_MISC(_type, *, read_region, _size)		\
1819170530Ssam	BHNDB_IO_MISC(_type, *, write_region, _size)		\
1820170530Ssam								\
1821170530Ssam	BHNDB_IO_MISC(_type, *, read_region_stream, _size)	\
1822170530Ssam	BHNDB_IO_MISC(_type, *, write_region_stream, _size)
1823170530Ssam
1824170530SsamBHNDB_IO_METHODS(uint8_t, 1);
1825170530SsamBHNDB_IO_METHODS(uint16_t, 2);
1826170530SsamBHNDB_IO_METHODS(uint32_t, 4);
1827170530Ssam
1828170530Ssam/**
1829170530Ssam * Default bhndb(4) implementation of BHND_BUS_BARRIER().
1830170530Ssam */
1831170530Ssamstatic void
1832170530Ssambhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
1833170530Ssam    bus_size_t offset, bus_size_t length, int flags)
1834170530Ssam{
1835170530Ssam	BHNDB_IO_COMMON_SETUP(length);
1836170530Ssam
1837170530Ssam	bus_barrier(io_res, io_offset + offset, length, flags);
1838170530Ssam
1839178354Ssam	BHNDB_IO_COMMON_TEARDOWN();
1840170530Ssam}
1841173273Ssam
1842173273Ssam/**
1843173273Ssam * Default bhndb(4) implementation of BUS_SETUP_INTR().
1844173273Ssam */
1845170530Ssamstatic int
1846170530Ssambhndb_setup_intr(device_t dev, device_t child, struct resource *r,
1847170530Ssam    int flags, driver_filter_t filter, driver_intr_t handler, void *arg,
1848170530Ssam    void **cookiep)
1849170530Ssam{
1850170530Ssam	// TODO
1851170530Ssam	return (EOPNOTSUPP);
1852178354Ssam}
1853170530Ssam
1854170530Ssam/**
1855170530Ssam * Default bhndb(4) implementation of BUS_TEARDOWN_INTR().
1856170530Ssam */
1857170530Ssamstatic int
1858170530Ssambhndb_teardown_intr(device_t dev, device_t child, struct resource *r,
1859170530Ssam    void *cookie)
1860170530Ssam{
1861170530Ssam	// TODO
1862170530Ssam	return (EOPNOTSUPP);
1863170530Ssam}
1864170530Ssam
1865170530Ssam/**
1866170530Ssam * Default bhndb(4) implementation of BUS_CONFIG_INTR().
1867180309Ssam */
1868170530Ssamstatic int
1869170530Ssambhndb_config_intr(device_t dev, int irq, enum intr_trigger trig,
1870170530Ssam    enum intr_polarity pol)
1871170530Ssam{
1872178354Ssam	// TODO
1873170530Ssam	return (EOPNOTSUPP);
1874170530Ssam}
1875170530Ssam
1876170530Ssam/**
1877170530Ssam * Default bhndb(4) implementation of BUS_BIND_INTR().
1878170530Ssam */
1879170530Ssamstatic int
1880170530Ssambhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu)
1881170530Ssam{
1882170530Ssam	// TODO
1883170530Ssam	return (EOPNOTSUPP);
1884178354Ssam}
1885170530Ssam
1886170530Ssam/**
1887178354Ssam * Default bhndb(4) implementation of BUS_DESCRIBE_INTR().
1888170530Ssam */
1889178354Ssamstatic int
1890170530Ssambhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie,
1891170530Ssam    const char *descr)
1892170530Ssam{
1893170530Ssam	// TODO
1894170530Ssam	return (EOPNOTSUPP);
1895170530Ssam}
1896170530Ssam
1897170530Ssam/**
1898170530Ssam * Default bhndb(4) implementation of BUS_GET_DMA_TAG().
1899178354Ssam */
1900170530Ssamstatic bus_dma_tag_t
1901170530Ssambhndb_get_dma_tag(device_t dev, device_t child)
1902170530Ssam{
1903170530Ssam	// TODO
1904170530Ssam	return (NULL);
1905170530Ssam}
1906170530Ssam
1907170530Ssamstatic device_method_t bhndb_methods[] = {
1908178354Ssam	/* Device interface */ \
1909170530Ssam	DEVMETHOD(device_probe,			bhndb_generic_probe),
1910170530Ssam	DEVMETHOD(device_detach,		bhndb_generic_detach),
1911178354Ssam	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
1912178354Ssam	DEVMETHOD(device_suspend,		bhndb_generic_suspend),
1913170530Ssam	DEVMETHOD(device_resume,		bhndb_generic_resume),
1914170530Ssam
1915170530Ssam	/* Bus interface */
1916170530Ssam	DEVMETHOD(bus_probe_nomatch,		bhndb_probe_nomatch),
1917170530Ssam	DEVMETHOD(bus_print_child,		bhndb_print_child),
1918170530Ssam	DEVMETHOD(bus_child_pnpinfo_str,	bhndb_child_pnpinfo_str),
1919170530Ssam	DEVMETHOD(bus_child_location_str,	bhndb_child_location_str),
1920170530Ssam	DEVMETHOD(bus_add_child,		bhndb_add_child),
1921170530Ssam	DEVMETHOD(bus_child_deleted,		bhndb_child_deleted),
1922170530Ssam
1923170530Ssam	DEVMETHOD(bus_alloc_resource,		bhndb_alloc_resource),
1924170530Ssam	DEVMETHOD(bus_release_resource,		bhndb_release_resource),
1925170530Ssam	DEVMETHOD(bus_activate_resource,	bhndb_activate_resource),
1926170530Ssam	DEVMETHOD(bus_deactivate_resource,	bhndb_deactivate_resource),
1927170530Ssam
1928170530Ssam	DEVMETHOD(bus_setup_intr,		bhndb_setup_intr),
1929170530Ssam	DEVMETHOD(bus_teardown_intr,		bhndb_teardown_intr),
1930170530Ssam	DEVMETHOD(bus_config_intr,		bhndb_config_intr),
1931170530Ssam	DEVMETHOD(bus_bind_intr,		bhndb_bind_intr),
1932170530Ssam	DEVMETHOD(bus_describe_intr,		bhndb_describe_intr),
1933170530Ssam
1934170530Ssam	DEVMETHOD(bus_get_dma_tag,		bhndb_get_dma_tag),
1935170530Ssam
1936170530Ssam	DEVMETHOD(bus_adjust_resource,		bhndb_adjust_resource),
1937170530Ssam	DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
1938170530Ssam	DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
1939170530Ssam	DEVMETHOD(bus_delete_resource,		bus_generic_rl_delete_resource),
1940170530Ssam	DEVMETHOD(bus_get_resource_list,	bhndb_get_resource_list),
1941170530Ssam
1942170530Ssam	DEVMETHOD(bus_read_ivar,		bhndb_read_ivar),
1943170530Ssam	DEVMETHOD(bus_write_ivar,		bhndb_write_ivar),
1944170530Ssam
1945170530Ssam	/* BHNDB interface */
1946170530Ssam	DEVMETHOD(bhndb_get_chipid,		bhndb_get_chipid),
1947178354Ssam	DEVMETHOD(bhndb_init_full_config,	bhndb_generic_init_full_config),
1948170530Ssam	DEVMETHOD(bhndb_find_hostb_device,	bhndb_find_hostb_device),
1949173865Ssam	DEVMETHOD(bhndb_suspend_resource,	bhndb_suspend_resource),
1950170530Ssam	DEVMETHOD(bhndb_resume_resource,	bhndb_resume_resource),
1951170530Ssam
1952178354Ssam	/* BHND interface */
1953173273Ssam	DEVMETHOD(bhnd_bus_is_hw_disabled,	bhndb_is_hw_disabled),
1954173273Ssam	DEVMETHOD(bhnd_bus_get_chipid,		bhndb_get_chipid),
1955173273Ssam	DEVMETHOD(bhnd_bus_activate_resource,	bhndb_activate_bhnd_resource),
1956173273Ssam	DEVMETHOD(bhnd_bus_deactivate_resource,	bhndb_deactivate_bhnd_resource),
1957173273Ssam	DEVMETHOD(bhnd_bus_get_nvram_var,	bhnd_bus_generic_get_nvram_var),
1958173273Ssam	DEVMETHOD(bhnd_bus_read_1,		bhndb_bus_read_1),
1959173273Ssam	DEVMETHOD(bhnd_bus_read_2,		bhndb_bus_read_2),
1960173273Ssam	DEVMETHOD(bhnd_bus_read_4,		bhndb_bus_read_4),
1961178354Ssam	DEVMETHOD(bhnd_bus_write_1,		bhndb_bus_write_1),
1962173273Ssam	DEVMETHOD(bhnd_bus_write_2,		bhndb_bus_write_2),
1963178354Ssam	DEVMETHOD(bhnd_bus_write_4,		bhndb_bus_write_4),
1964173273Ssam
1965173273Ssam	DEVMETHOD(bhnd_bus_read_stream_1,	bhndb_bus_read_stream_1),
1966173273Ssam	DEVMETHOD(bhnd_bus_read_stream_2,	bhndb_bus_read_stream_2),
1967173865Ssam	DEVMETHOD(bhnd_bus_read_stream_4,	bhndb_bus_read_stream_4),
1968173865Ssam	DEVMETHOD(bhnd_bus_write_stream_1,	bhndb_bus_write_stream_1),
1969173865Ssam	DEVMETHOD(bhnd_bus_write_stream_2,	bhndb_bus_write_stream_2),
1970173273Ssam	DEVMETHOD(bhnd_bus_write_stream_4,	bhndb_bus_write_stream_4),
1971173273Ssam
1972178354Ssam	DEVMETHOD(bhnd_bus_read_multi_1,	bhndb_bus_read_multi_1),
1973173273Ssam	DEVMETHOD(bhnd_bus_read_multi_2,	bhndb_bus_read_multi_2),
1974173273Ssam	DEVMETHOD(bhnd_bus_read_multi_4,	bhndb_bus_read_multi_4),
1975173273Ssam	DEVMETHOD(bhnd_bus_write_multi_1,	bhndb_bus_write_multi_1),
1976178354Ssam	DEVMETHOD(bhnd_bus_write_multi_2,	bhndb_bus_write_multi_2),
1977178354Ssam	DEVMETHOD(bhnd_bus_write_multi_4,	bhndb_bus_write_multi_4),
1978173273Ssam
1979170530Ssam	DEVMETHOD(bhnd_bus_read_multi_stream_1,	bhndb_bus_read_multi_stream_1),
1980178354Ssam	DEVMETHOD(bhnd_bus_read_multi_stream_2,	bhndb_bus_read_multi_stream_2),
1981170530Ssam	DEVMETHOD(bhnd_bus_read_multi_stream_4,	bhndb_bus_read_multi_stream_4),
1982178354Ssam	DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1),
1983170530Ssam	DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2),
1984170530Ssam	DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4),
1985170530Ssam
1986170530Ssam	DEVMETHOD(bhnd_bus_set_multi_1,		bhndb_bus_set_multi_1),
1987170530Ssam	DEVMETHOD(bhnd_bus_set_multi_2,		bhndb_bus_set_multi_2),
1988173865Ssam	DEVMETHOD(bhnd_bus_set_multi_4,		bhndb_bus_set_multi_4),
1989173865Ssam	DEVMETHOD(bhnd_bus_set_region_1,	bhndb_bus_set_region_1),
1990173273Ssam	DEVMETHOD(bhnd_bus_set_region_2,	bhndb_bus_set_region_2),
1991170530Ssam	DEVMETHOD(bhnd_bus_set_region_4,	bhndb_bus_set_region_4),
1992170530Ssam
1993170530Ssam	DEVMETHOD(bhnd_bus_read_region_1,	bhndb_bus_read_region_1),
1994170530Ssam	DEVMETHOD(bhnd_bus_read_region_2,	bhndb_bus_read_region_2),
1995170530Ssam	DEVMETHOD(bhnd_bus_read_region_4,	bhndb_bus_read_region_4),
1996170530Ssam	DEVMETHOD(bhnd_bus_write_region_1,	bhndb_bus_write_region_1),
1997170530Ssam	DEVMETHOD(bhnd_bus_write_region_2,	bhndb_bus_write_region_2),
1998173273Ssam	DEVMETHOD(bhnd_bus_write_region_4,	bhndb_bus_write_region_4),
1999173273Ssam
2000173273Ssam	DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1),
2001173273Ssam	DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2),
2002173273Ssam	DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4),
2003173273Ssam	DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1),
2004173273Ssam	DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2),
2005170530Ssam	DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4),
2006170530Ssam
2007170530Ssam	DEVMETHOD(bhnd_bus_barrier,		bhndb_bus_barrier),
2008170530Ssam
2009170530Ssam	DEVMETHOD_END
2010170530Ssam};
2011170530Ssam
2012170530Ssamdevclass_t bhndb_devclass;
2013170530Ssam
2014170530SsamDEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc));
2015170530Ssam
2016170530SsamMODULE_VERSION(bhndb, 1);
2017170530SsamMODULE_DEPEND(bhndb, bhnd, 1, 1, 1);
2018170530SsamMODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1);
2019170530Ssam