bhndb.c revision 300250
1184138Smav/*-
2184138Smav * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3184138Smav * All rights reserved.
4184138Smav *
5184138Smav * Redistribution and use in source and binary forms, with or without
6184138Smav * modification, are permitted provided that the following conditions
7184138Smav * are met:
8184138Smav * 1. Redistributions of source code must retain the above copyright
9184138Smav *    notice, this list of conditions and the following disclaimer,
10184138Smav *    without modification.
11184138Smav * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12184138Smav *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13184138Smav *    redistribution must be conditioned upon including a substantially
14184138Smav *    similar Disclaimer requirement for further binary redistribution.
15184138Smav *
16184138Smav * NO WARRANTY
17184138Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18184138Smav * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19184138Smav * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20184138Smav * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21184138Smav * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22184138Smav * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23184138Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24184138Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25184138Smav * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26184138Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27184138Smav * THE POSSIBILITY OF SUCH DAMAGES.
28184138Smav */
29184138Smav
30184138Smav#include <sys/cdefs.h>
31184138Smav__FBSDID("$FreeBSD: head/sys/dev/bhnd/bhndb/bhndb.c 300250 2016-05-20 00:45:16Z adrian $");
32266200Sian
33184138Smav/*
34184138Smav * Abstract BHND Bridge Device Driver
35184138Smav *
36184138Smav * Provides generic support for bridging from a parent bus (such as PCI) to
37184138Smav * a BHND-compatible bus (e.g. bcma or siba).
38184138Smav */
39184138Smav
40187876Smav#include <sys/param.h>
41184138Smav#include <sys/kernel.h>
42184138Smav#include <sys/bus.h>
43184138Smav#include <sys/module.h>
44184138Smav#include <sys/systm.h>
45184138Smav
46184138Smav#include <machine/bus.h>
47184138Smav#include <sys/rman.h>
48184138Smav#include <machine/resource.h>
49184138Smav
50184138Smav#include <dev/bhnd/bhndvar.h>
51184138Smav#include <dev/bhnd/bhndreg.h>
52184138Smav
53241600Sgonzo#include <dev/bhnd/cores/chipc/chipcreg.h>
54184138Smav#include <dev/bhnd/nvram/bhnd_nvram.h>
55271051Smarius
56184138Smav#include "bhnd_chipc_if.h"
57271051Smarius#include "bhnd_nvram_if.h"
58187876Smav
59271051Smarius#include "bhndbvar.h"
60187876Smav#include "bhndb_bus_if.h"
61241600Sgonzo#include "bhndb_hwdata.h"
62241600Sgonzo#include "bhndb_private.h"
63241600Sgonzo
64241600Sgonzo/* Debugging flags */
65241600Sgonzostatic u_long bhndb_debug = 0;
66184138SmavTUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug);
67241600Sgonzo
68241600Sgonzoenum {
69241600Sgonzo	BHNDB_DEBUG_PRIO = 1 << 0,
70241600Sgonzo};
71241600Sgonzo
72184138Smav#define	BHNDB_DEBUG(_type)	(BHNDB_DEBUG_ ## _type & bhndb_debug)
73184138Smav
74184138Smavstatic bool			 bhndb_hw_matches(device_t *devlist,
75184138Smav				     int num_devs,
76184138Smav				     const struct bhndb_hw *hw);
77184138Smav
78184138Smavstatic int			 bhndb_initialize_region_cfg(
79184138Smav				     struct bhndb_softc *sc, device_t *devs,
80184138Smav				     int ndevs,
81184138Smav				     const struct bhndb_hw_priority *table,
82184138Smav				     struct bhndb_resources *r);
83184138Smav
84184138Smavstatic int			 bhndb_find_hwspec(struct bhndb_softc *sc,
85184138Smav				     device_t *devs, int ndevs,
86184138Smav				     const struct bhndb_hw **hw);
87184138Smav
88243689Sgonzostatic int			 bhndb_read_chipid(struct bhndb_softc *sc,
89243689Sgonzo				     const struct bhndb_hwcfg *cfg,
90246886Sgonzo				     struct bhnd_chipid *result);
91246886Sgonzo
92246886Sgonzobhndb_addrspace			 bhndb_get_addrspace(struct bhndb_softc *sc,
93241600Sgonzo				     device_t child);
94241600Sgonzo
95241600Sgonzostatic struct rman		*bhndb_get_rman(struct bhndb_softc *sc,
96241600Sgonzo				     device_t child, int type);
97241600Sgonzo
98241600Sgonzostatic int			 bhndb_init_child_resource(struct resource *r,
99241600Sgonzo				     struct resource *parent,
100241600Sgonzo				     bhnd_size_t offset,
101241600Sgonzo				     bhnd_size_t size);
102241600Sgonzo
103184138Smavstatic int			 bhndb_activate_static_region(
104184138Smav				     struct bhndb_softc *sc,
105184138Smav				     struct bhndb_region *region,
106184138Smav				     device_t child, int type, int rid,
107184138Smav				     struct resource *r);
108184138Smav
109184138Smavstatic int			 bhndb_try_activate_resource(
110241600Sgonzo				     struct bhndb_softc *sc, device_t child,
111184138Smav				     int type, int rid, struct resource *r,
112184138Smav				     bool *indirect);
113184138Smav
114184138Smav
115184138Smav/**
116184138Smav * Default bhndb(4) implementation of DEVICE_PROBE().
117184138Smav *
118184138Smav * This function provides the default bhndb implementation of DEVICE_PROBE(),
119184138Smav * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge().
120184138Smav */
121184138Smavint
122184138Smavbhndb_generic_probe(device_t dev)
123184138Smav{
124184138Smav	return (BUS_PROBE_NOWILDCARD);
125184138Smav}
126184138Smav
127184138Smavstatic void
128184138Smavbhndb_probe_nomatch(device_t dev, device_t child)
129184138Smav{
130184138Smav	const char *name;
131184138Smav
132184138Smav	name = device_get_name(child);
133184138Smav	if (name == NULL)
134184138Smav		name = "unknown device";
135184138Smav
136184138Smav	device_printf(dev, "<%s> (no driver attached)\n", name);
137184138Smav}
138184138Smav
139184138Smavstatic int
140184138Smavbhndb_print_child(device_t dev, device_t child)
141184138Smav{
142184138Smav	struct bhndb_softc	*sc;
143184138Smav	struct resource_list	*rl;
144184138Smav	int			 retval = 0;
145184138Smav
146184138Smav	sc = device_get_softc(dev);
147184138Smav
148184138Smav	retval += bus_print_child_header(dev, child);
149184138Smav
150184138Smav	rl = BUS_GET_RESOURCE_LIST(dev, child);
151184138Smav	if (rl != NULL) {
152184138Smav		retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
153184138Smav		    "%#jx");
154241600Sgonzo		retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ,
155184138Smav		    "%jd");
156184138Smav	}
157184138Smav
158184138Smav	retval += bus_print_child_domain(dev, child);
159184138Smav	retval += bus_print_child_footer(dev, child);
160184138Smav
161184138Smav	return (retval);
162241600Sgonzo}
163184138Smav
164184138Smavstatic int
165184138Smavbhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf,
166184138Smav    size_t buflen)
167184138Smav{
168184138Smav	*buf = '\0';
169184138Smav	return (0);
170184138Smav}
171185661Smav
172184138Smavstatic int
173185661Smavbhndb_child_location_str(device_t dev, device_t child, char *buf,
174185661Smav    size_t buflen)
175184138Smav{
176276287Sian	struct bhndb_softc *sc;
177276287Sian
178276287Sian	sc = device_get_softc(dev);
179276287Sian
180276287Sian	snprintf(buf, buflen, "base=0x%llx",
181276287Sian	    (unsigned long long) sc->chipid.enum_addr);
182276287Sian	return (0);
183276287Sian}
184276287Sian
185276287Sian/**
186276287Sian * Return true if @p devlist matches the @p hw specification.
187276287Sian *
188276287Sian * @param devlist A device table to match against.
189276287Sian * @param num_devs The number of devices in @p devlist.
190276287Sian * @param hw The hardware description to be matched against.
191276287Sian */
192276287Sianstatic bool
193276287Sianbhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw)
194276287Sian{
195276287Sian	for (u_int i = 0; i < hw->num_hw_reqs; i++) {
196184138Smav		const struct bhnd_core_match	*match;
197276287Sian		bool				 found;
198184138Smav
199276287Sian		match =  &hw->hw_reqs[i];
200276287Sian		found = false;
201276287Sian
202276287Sian		for (int d = 0; d < num_devs; d++) {
203184138Smav			if (!bhnd_device_matches(devlist[d], match))
204184138Smav				continue;
205184138Smav
206184138Smav			found = true;
207276287Sian			break;
208184138Smav		}
209184138Smav
210184138Smav		if (!found)
211184138Smav			return (false);
212184138Smav	}
213184138Smav
214184138Smav	return (true);
215184138Smav}
216184138Smav
217184138Smav/**
218184138Smav * Initialize the region maps and priority configuration in @p r using
219184138Smav * the provided priority @p table and the set of devices attached to
220184138Smav * the bridged @p bus_dev .
221184138Smav *
222184138Smav * @param sc The bhndb device state.
223184138Smav * @param devs All devices enumerated on the bridged bhnd bus.
224184138Smav * @param ndevs The length of @p devs.
225184138Smav * @param table Hardware priority table to be used to determine the relative
226184138Smav * priorities of per-core port resources.
227184138Smav * @param r The resource state to be configured.
228184138Smav */
229184138Smavstatic int
230184138Smavbhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs,
231184138Smav    const struct bhndb_hw_priority *table, struct bhndb_resources *r)
232184138Smav{
233184138Smav	const struct bhndb_hw_priority	*hp;
234242320Sgonzo	bhnd_addr_t			 addr;
235184138Smav	bhnd_size_t			 size;
236184138Smav	size_t				 prio_low, prio_default, prio_high;
237184138Smav	int				 error;
238184138Smav
239184138Smav	/* The number of port regions per priority band that must be accessible
240184138Smav	 * via dynamic register windows */
241184138Smav	prio_low = 0;
242266751Sian	prio_default = 0;
243266751Sian	prio_high = 0;
244184138Smav
245184138Smav	/*
246184138Smav	 * Register bridge regions covering all statically mapped ports.
247254423Sian	 */
248254423Sian	for (int i = 0; i < ndevs; i++) {
249254423Sian		const struct bhndb_regwin	*regw;
250254423Sian		device_t			 child;
251254423Sian
252242320Sgonzo		child = devs[i];
253242320Sgonzo
254242320Sgonzo		for (regw = r->cfg->register_windows;
255246886Sgonzo		    regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
256242320Sgonzo		{
257242320Sgonzo			/* Only core windows are supported */
258242320Sgonzo			if (regw->win_type != BHNDB_REGWIN_T_CORE)
259242320Sgonzo				continue;
260242320Sgonzo
261242320Sgonzo			/* Skip non-applicable register windows. */
262184138Smav			if (!bhndb_regwin_matches_device(regw, child))
263242320Sgonzo				continue;
264242320Sgonzo
265246886Sgonzo			/* Fetch the base address of the mapped port. */
266246886Sgonzo			error = bhnd_get_region_addr(child,
267242320Sgonzo			    regw->d.core.port_type, regw->d.core.port,
268246886Sgonzo			    regw->d.core.region, &addr, &size);
269242320Sgonzo			if (error)
270242320Sgonzo			    return (error);
271242320Sgonzo
272242320Sgonzo			/*
273242320Sgonzo			 * Always defer to the register window's size.
274242320Sgonzo			 *
275242320Sgonzo			 * If the port size is smaller than the window size,
276242320Sgonzo			 * this ensures that we fully utilize register windows
277242320Sgonzo			 * larger than the referenced port.
278242320Sgonzo			 *
279242320Sgonzo			 * If the port size is larger than the window size, this
280184138Smav			 * ensures that we do not directly map the allocations
281242320Sgonzo			 * within the region to a too-small window.
282242320Sgonzo			 */
283242320Sgonzo			size = regw->win_size;
284242320Sgonzo
285184138Smav			/*
286184138Smav			 * Add to the bus region list.
287184138Smav			 *
288184138Smav			 * The window priority for a statically mapped
289184138Smav			 * region is always HIGH.
290184138Smav			 */
291184138Smav			error = bhndb_add_resource_region(r, addr, size,
292184138Smav			    BHNDB_PRIORITY_HIGH, regw);
293184138Smav			if (error)
294184138Smav				return (error);
295184138Smav		}
296184138Smav	}
297184138Smav
298184138Smav	/*
299184138Smav	 * Perform priority accounting and register bridge regions for all
300184138Smav	 * ports defined in the priority table
301184138Smav	 */
302184138Smav	for (int i = 0; i < ndevs; i++) {
303184138Smav		struct bhndb_region	*region;
304184138Smav		device_t		 child;
305184138Smav
306184138Smav		child = devs[i];
307184138Smav
308184138Smav		/*
309184138Smav		 * Skip priority accounting for cores that ...
310184138Smav		 */
311184138Smav
312184138Smav		/* ... do not require bridge resources */
313184138Smav		if (bhnd_is_hw_disabled(child) || !device_is_enabled(child))
314241600Sgonzo			continue;
315184138Smav
316184138Smav		/* ... do not have a priority table entry */
317184138Smav		hp = bhndb_hw_priority_find_device(table, child);
318184138Smav		if (hp == NULL)
319184138Smav			continue;
320184138Smav
321184138Smav		/* ... are explicitly disabled in the priority table. */
322184138Smav		if (hp->priority == BHNDB_PRIORITY_NONE)
323184138Smav			continue;
324184138Smav
325184138Smav		/* Determine the number of dynamic windows required and
326184138Smav		 * register their bus_region entries. */
327184138Smav		for (u_int i = 0; i < hp->num_ports; i++) {
328184138Smav			const struct bhndb_port_priority *pp;
329184138Smav
330184138Smav			pp = &hp->ports[i];
331184138Smav
332184138Smav			/* Skip ports not defined on this device */
333184138Smav			if (!bhnd_is_region_valid(child, pp->type, pp->port,
334184138Smav			    pp->region))
335184138Smav			{
336184138Smav				continue;
337184138Smav			}
338184138Smav
339184138Smav			/* Fetch the address+size of the mapped port. */
340184138Smav			error = bhnd_get_region_addr(child, pp->type, pp->port,
341184138Smav			    pp->region, &addr, &size);
342184138Smav			if (error)
343184138Smav			    return (error);
344184138Smav
345184138Smav			/* Skip ports with an existing static mapping */
346184138Smav			region = bhndb_find_resource_region(r, addr, size);
347184138Smav			if (region != NULL && region->static_regwin != NULL)
348184138Smav				continue;
349184138Smav
350184138Smav			/* Define a dynamic region for this port */
351184138Smav			error = bhndb_add_resource_region(r, addr, size,
352184138Smav			    pp->priority, NULL);
353184138Smav			if (error)
354184138Smav				return (error);
355184138Smav
356184138Smav			/* Update port mapping counts */
357241600Sgonzo			switch (pp->priority) {
358184138Smav			case BHNDB_PRIORITY_NONE:
359254512Srpaulo				break;
360184138Smav			case BHNDB_PRIORITY_LOW:
361184138Smav				prio_low++;
362184138Smav				break;
363184138Smav			case BHNDB_PRIORITY_DEFAULT:
364184138Smav				prio_default++;
365184138Smav				break;
366184138Smav			case BHNDB_PRIORITY_HIGH:
367184138Smav				prio_high++;
368184138Smav				break;
369184138Smav			}
370184138Smav		}
371241600Sgonzo	}
372184138Smav
373184138Smav	/* Determine the minimum priority at which we'll allocate direct
374184138Smav	 * register windows from our dynamic pool */
375184138Smav	size_t prio_total = prio_low + prio_default + prio_high;
376184138Smav	if (prio_total <= r->dwa_count) {
377184138Smav		/* low+default+high priority regions get windows */
378184138Smav		r->min_prio = BHNDB_PRIORITY_LOW;
379184138Smav
380184138Smav	} else if (prio_default + prio_high <= r->dwa_count) {
381184138Smav		/* default+high priority regions get windows */
382184138Smav		r->min_prio = BHNDB_PRIORITY_DEFAULT;
383184138Smav
384184138Smav	} else {
385184138Smav		/* high priority regions get windows */
386184138Smav		r->min_prio = BHNDB_PRIORITY_HIGH;
387184138Smav	}
388184138Smav
389184138Smav	if (BHNDB_DEBUG(PRIO)) {
390184138Smav		struct bhndb_region	*region;
391184138Smav		const char		*direct_msg, *type_msg;
392184138Smav		bhndb_priority_t	 prio, prio_min;
393184138Smav
394184138Smav		prio_min = r->min_prio;
395184138Smav		device_printf(sc->dev, "min_prio: %d\n", prio_min);
396184138Smav
397184138Smav		STAILQ_FOREACH(region, &r->bus_regions, link) {
398184138Smav			prio = region->priority;
399254512Srpaulo
400184138Smav			direct_msg = prio >= prio_min ? "direct" : "indirect";
401184138Smav			type_msg = region->static_regwin ? "static" : "dynamic";
402184138Smav
403184138Smav			device_printf(sc->dev, "region 0x%llx+0x%llx priority "
404184138Smav			    "%u %s/%s\n",
405184138Smav			    (unsigned long long) region->addr,
406184138Smav			    (unsigned long long) region->size,
407184138Smav			    region->priority,
408184138Smav			    direct_msg, type_msg);
409184138Smav		}
410184138Smav	}
411241600Sgonzo
412184138Smav	return (0);
413184138Smav}
414184138Smav
415184138Smav/**
416184138Smav * Find a hardware specification for @p dev.
417184138Smav *
418184138Smav * @param sc The bhndb device state.
419184138Smav * @param devs All devices enumerated on the bridged bhnd bus.
420184138Smav * @param ndevs The length of @p devs.
421184138Smav * @param[out] hw On success, the matched hardware specification.
422184138Smav * with @p dev.
423184138Smav *
424184138Smav * @retval 0 success
425184138Smav * @retval non-zero if an error occurs fetching device info for comparison.
426184138Smav */
427184138Smavstatic int
428184138Smavbhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs,
429184138Smav    const struct bhndb_hw **hw)
430184138Smav{
431184138Smav	const struct bhndb_hw	*next, *hw_table;
432184138Smav
433184138Smav	/* Search for the first matching hardware config. */
434184138Smav	hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev);
435184138Smav	for (next = hw_table; next->hw_reqs != NULL; next++) {
436184138Smav		if (!bhndb_hw_matches(devs, ndevs, next))
437184138Smav			continue;
438184138Smav
439184138Smav		/* Found */
440184138Smav		*hw = next;
441184138Smav		return (0);
442184138Smav	}
443184138Smav
444184138Smav	return (ENOENT);
445184138Smav}
446184138Smav
447184138Smav/**
448184138Smav * Read the ChipCommon identification data for this device.
449184138Smav *
450184138Smav * @param sc bhndb device state.
451184138Smav * @param cfg The hardware configuration to use when mapping the ChipCommon
452184138Smav * registers.
453184138Smav * @param[out] result the chip identification data.
454184138Smav *
455184138Smav * @retval 0 success
456184138Smav * @retval non-zero if the ChipCommon identification data could not be read.
457184138Smav */
458184138Smavstatic int
459184138Smavbhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg,
460184138Smav    struct bhnd_chipid *result)
461184138Smav{
462184138Smav	const struct bhnd_chipid	*parent_cid;
463184138Smav	const struct bhndb_regwin	*cc_win;
464184138Smav	struct resource_spec		 rs;
465241600Sgonzo	int				 error;
466184138Smav
467184138Smav	/* Let our parent device override the discovery process */
468184138Smav	parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev);
469184138Smav	if (parent_cid != NULL) {
470184138Smav		*result = *parent_cid;
471184138Smav		return (0);
472184138Smav	}
473184138Smav
474184138Smav	/* Find a register window we can use to map the first CHIPC_CHIPID_SIZE
475184138Smav	 * of ChipCommon registers. */
476184138Smav	cc_win = bhndb_regwin_find_best(cfg->register_windows,
477241600Sgonzo	    BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE);
478184138Smav	if (cc_win == NULL) {
479184138Smav		device_printf(sc->dev, "no chipcommon register window\n");
480184138Smav		return (0);
481184138Smav	}
482184138Smav
483241600Sgonzo	/* We can assume a device without a static ChipCommon window uses the
484241600Sgonzo	 * default ChipCommon address. */
485184138Smav	if (cc_win->win_type == BHNDB_REGWIN_T_DYN) {
486254507Sian		error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win,
487241600Sgonzo		    BHND_DEFAULT_CHIPC_ADDR);
488184138Smav
489241600Sgonzo		if (error) {
490241600Sgonzo			device_printf(sc->dev, "failed to set chipcommon "
491241600Sgonzo			    "register window\n");
492184138Smav			return (error);
493241600Sgonzo		}
494241600Sgonzo	}
495241600Sgonzo
496241600Sgonzo	/* Let the default bhnd implemenation alloc/release the resource and
497241600Sgonzo	 * perform the read */
498241600Sgonzo	rs.type = cc_win->res.type;
499241600Sgonzo	rs.rid = cc_win->res.rid;
500241600Sgonzo	rs.flags = RF_ACTIVE;
501241600Sgonzo
502241600Sgonzo	return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset,
503241600Sgonzo	    result));
504184138Smav}
505241600Sgonzo
506241600Sgonzo/**
507241600Sgonzo * Helper function that must be called by subclass bhndb(4) drivers
508241600Sgonzo * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4)
509241600Sgonzo * APIs on the bridge device.
510241600Sgonzo *
511241600Sgonzo * @param dev The bridge device to attach.
512184138Smav * @param bridge_devclass The device class of the bridging core. This is used
513241600Sgonzo * to automatically detect the bridge core, and to disable additional bridge
514241600Sgonzo * cores (e.g. PCMCIA on a PCIe device).
515241600Sgonzo */
516241600Sgonzoint
517241600Sgonzobhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass)
518241600Sgonzo{
519241600Sgonzo	struct bhndb_devinfo		*dinfo;
520241600Sgonzo	struct bhndb_softc		*sc;
521241600Sgonzo	const struct bhndb_hwcfg	*cfg;
522241600Sgonzo	int				 error;
523241600Sgonzo
524184138Smav	sc = device_get_softc(dev);
525184138Smav	sc->dev = dev;
526241600Sgonzo	sc->parent_dev = device_get_parent(dev);
527241600Sgonzo	sc->bridge_class = bridge_devclass;
528241600Sgonzo
529241600Sgonzo	BHNDB_LOCK_INIT(sc);
530242320Sgonzo
531242320Sgonzo	/* Read our chip identification data */
532242320Sgonzo	cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev);
533242320Sgonzo	if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid)))
534241600Sgonzo		return (error);
535243689Sgonzo
536254507Sian	/* Populate generic resource allocation state. */
537254507Sian	sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg);
538243689Sgonzo	if (sc->bus_res == NULL) {
539254507Sian		return (ENXIO);
540254507Sian	}
541254507Sian
542254507Sian	/* Attach our bridged bus device */
543254507Sian	sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1);
544254507Sian	if (sc->bus_dev == NULL) {
545254507Sian		error = ENXIO;
546254507Sian		goto failed;
547254507Sian	}
548241600Sgonzo
549254507Sian	/* Configure address space */
550241600Sgonzo	dinfo = device_get_ivars(sc->bus_dev);
551243689Sgonzo	dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
552241600Sgonzo
553241600Sgonzo	/* Finish attach */
554242320Sgonzo	return (bus_generic_attach(dev));
555242320Sgonzo
556242320Sgonzofailed:
557242320Sgonzo	BHNDB_LOCK_DESTROY(sc);
558242320Sgonzo
559242320Sgonzo	if (sc->bus_res != NULL)
560242320Sgonzo		bhndb_free_resources(sc->bus_res);
561242320Sgonzo
562254507Sian	return (error);
563254507Sian}
564254507Sian
565254507Sian/**
566254507Sian * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG().
567241600Sgonzo *
568241600Sgonzo * This function provides the default bhndb implementation of
569254423Sian * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver
570254423Sian * overriding BHNDB_INIT_FULL_CONFIG().
571241600Sgonzo *
572184138Smav * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final
573246886Sgonzo * bridge configuration based on the hardware information enumerated by the
574241600Sgonzo * child bus, and will reset all resource allocation state on the bridge.
575241600Sgonzo *
576241600Sgonzo * When calling this method:
577241600Sgonzo * - Any bus resources previously allocated by @p child must be deallocated.
578241600Sgonzo * - The @p child bus must have performed initial enumeration -- but not
579241600Sgonzo *   probe or attachment -- of its children.
580241600Sgonzo */
581241600Sgonzoint
582241600Sgonzobhndb_generic_init_full_config(device_t dev, device_t child,
583241600Sgonzo    const struct bhndb_hw_priority *hw_prio_table)
584241600Sgonzo{
585184138Smav	struct bhndb_softc		*sc;
586241600Sgonzo	const struct bhndb_hw		*hw;
587283318Sian	struct bhndb_resources		*r;
588283318Sian	device_t			*devs;
589241600Sgonzo	device_t			 hostb;
590241600Sgonzo	int				 ndevs;
591241600Sgonzo	int				 error;
592241600Sgonzo
593241600Sgonzo	sc = device_get_softc(dev);
594184138Smav	hostb = NULL;
595241600Sgonzo
596241600Sgonzo	/* Fetch the full set of bhnd-attached cores */
597241600Sgonzo	if ((error = device_get_children(sc->bus_dev, &devs, &ndevs)))
598241600Sgonzo		return (error);
599241600Sgonzo
600247495Sgonzo	/* Find our host bridge device */
601247495Sgonzo	hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child);
602247495Sgonzo	if (hostb == NULL) {
603247495Sgonzo		device_printf(sc->dev, "no host bridge core found\n");
604247495Sgonzo		error = ENODEV;
605247495Sgonzo		goto cleanup;
606247495Sgonzo	}
607241600Sgonzo
608283318Sian	/* Find our full register window configuration */
609241600Sgonzo	if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) {
610241600Sgonzo		device_printf(sc->dev, "unable to identify device, "
611283318Sian			" using generic bridge resource definitions\n");
612283318Sian		error = 0;
613241600Sgonzo		goto cleanup;
614241600Sgonzo	}
615241600Sgonzo
616241600Sgonzo	if (bootverbose || BHNDB_DEBUG(PRIO))
617241600Sgonzo		device_printf(sc->dev, "%s resource configuration\n", hw->name);
618184138Smav
619241600Sgonzo	/* Release existing resource state */
620241600Sgonzo	BHNDB_LOCK(sc);
621241600Sgonzo	bhndb_free_resources(sc->bus_res);
622266200Sian	sc->bus_res = NULL;
623184138Smav	BHNDB_UNLOCK(sc);
624184138Smav
625184138Smav	/* Allocate new resource state */
626241600Sgonzo	r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg);
627241600Sgonzo	if (r == NULL) {
628184138Smav		error = ENXIO;
629241600Sgonzo		goto cleanup;
630241600Sgonzo	}
631184138Smav
632241600Sgonzo	/* Initialize our resource priority configuration */
633241600Sgonzo	error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r);
634241600Sgonzo	if (error) {
635241600Sgonzo		bhndb_free_resources(r);
636184138Smav		goto cleanup;
637266200Sian	}
638241600Sgonzo
639241600Sgonzo	/* Update our bridge state */
640184138Smav	BHNDB_LOCK(sc);
641241600Sgonzo	sc->bus_res = r;
642241600Sgonzo	sc->hostb_dev = hostb;
643241600Sgonzo	BHNDB_UNLOCK(sc);
644241600Sgonzo
645241600Sgonzocleanup:
646241600Sgonzo	free(devs, M_TEMP);
647184138Smav	return (error);
648241600Sgonzo}
649241600Sgonzo
650241600Sgonzo/**
651241600Sgonzo * Default bhndb(4) implementation of DEVICE_DETACH().
652241600Sgonzo *
653241600Sgonzo * This function detaches any child devices, and if successful, releases all
654184138Smav * resources held by the bridge device.
655241600Sgonzo */
656241600Sgonzoint
657184138Smavbhndb_generic_detach(device_t dev)
658184138Smav{
659184138Smav	struct bhndb_softc	*sc;
660241600Sgonzo	int			 error;
661241600Sgonzo
662185527Smav	sc = device_get_softc(dev);
663241600Sgonzo
664185527Smav	/* Detach children */
665185527Smav	if ((error = bus_generic_detach(dev)))
666185527Smav		return (error);
667185527Smav
668241600Sgonzo	/* Clean up our driver state. */
669241600Sgonzo	bhndb_free_resources(sc->bus_res);
670185527Smav
671241600Sgonzo	BHNDB_LOCK_DESTROY(sc);
672185527Smav
673241600Sgonzo	return (0);
674185527Smav}
675185527Smav
676246886Sgonzo/**
677246886Sgonzo * Default bhndb(4) implementation of DEVICE_SUSPEND().
678246886Sgonzo *
679246886Sgonzo * This function calls bus_generic_suspend() (or implements equivalent
680246886Sgonzo * behavior).
681246886Sgonzo */
682246886Sgonzoint
683246886Sgonzobhndb_generic_suspend(device_t dev)
684246886Sgonzo{
685241600Sgonzo	return (bus_generic_suspend(dev));
686241600Sgonzo}
687184138Smav
688184138Smav/**
689184138Smav * Default bhndb(4) implementation of DEVICE_RESUME().
690184138Smav *
691184138Smav * This function calls bus_generic_resume() (or implements equivalent
692184138Smav * behavior).
693184138Smav */
694184138Smavint
695184138Smavbhndb_generic_resume(device_t dev)
696184138Smav{
697184138Smav	struct bhndb_softc	*sc;
698184138Smav	struct bhndb_resources	*bus_res;
699283318Sian	struct bhndb_dw_alloc	*dwa;
700283318Sian	int			 error;
701283318Sian
702283318Sian	sc = device_get_softc(dev);
703283318Sian	bus_res = sc->bus_res;
704283318Sian
705184138Smav	/* Guarantee that all in-use dynamic register windows are mapped to
706283318Sian	 * their previously configured target address. */
707283318Sian	BHNDB_LOCK(sc);
708184138Smav	for (size_t i = 0; i < bus_res->dwa_count; i++) {
709283318Sian		dwa = &bus_res->dw_alloc[i];
710283318Sian
711283318Sian		/* Skip regions that were not previously used */
712278703Sian		if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0)
713278703Sian			continue;
714184138Smav
715184138Smav		/* Otherwise, ensure the register window is correct before
716184138Smav		 * any children attempt MMIO */
717184138Smav		error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target);
718184138Smav		if (error)
719241600Sgonzo			break;
720184138Smav	}
721184138Smav	BHNDB_UNLOCK(sc);
722184138Smav
723184138Smav	/* Error restoring hardware state; children cannot be safely resumed */
724184138Smav	if (error) {
725184138Smav		device_printf(dev, "Unable to restore hardware configuration; "
726266200Sian		    "cannot resume: %d\n", error);
727266200Sian		return (error);
728266200Sian	}
729266200Sian
730266200Sian	return (bus_generic_resume(dev));
731266200Sian}
732266200Sian
733266200Sian/**
734266200Sian * Default implementation of BHNDB_SUSPEND_RESOURCE.
735266200Sian */
736266200Sianstatic void
737266200Sianbhndb_suspend_resource(device_t dev, device_t child, int type,
738266200Sian    struct resource *r)
739266200Sian{
740266200Sian	struct bhndb_softc	*sc;
741266200Sian	struct bhndb_dw_alloc	*dwa;
742266200Sian
743266200Sian	sc = device_get_softc(dev);
744266200Sian
745266200Sian	// TODO: IRQs?
746276287Sian	if (type != SYS_RES_MEMORY)
747276287Sian		return;
748266200Sian
749266200Sian	BHNDB_LOCK(sc);
750266200Sian	dwa = bhndb_dw_find_resource(sc->bus_res, r);
751276287Sian	if (dwa == NULL) {
752276287Sian		BHNDB_UNLOCK(sc);
753266200Sian		return;
754266200Sian	}
755266200Sian
756184138Smav	if (BHNDB_DEBUG(PRIO))
757184138Smav		device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n",
758184138Smav		    type, rman_get_start(r), rman_get_size(r));
759184138Smav
760184138Smav	/* Release the resource's window reference */
761184138Smav	bhndb_dw_release(sc->bus_res, dwa, r);
762184138Smav	BHNDB_UNLOCK(sc);
763184138Smav}
764184138Smav
765184138Smav/**
766184138Smav * Default implementation of BHNDB_RESUME_RESOURCE.
767184138Smav */
768184138Smavstatic int
769184138Smavbhndb_resume_resource(device_t dev, device_t child, int type,
770184138Smav    struct resource *r)
771184138Smav{
772184138Smav	struct bhndb_softc	*sc;
773184138Smav
774184138Smav	sc = device_get_softc(dev);
775184138Smav
776184138Smav	// TODO: IRQs?
777184138Smav	if (type != SYS_RES_MEMORY)
778184138Smav		return (0);
779184138Smav
780184138Smav	/* Inactive resources don't require reallocation of bridge resources */
781184138Smav	if (!(rman_get_flags(r) & RF_ACTIVE))
782184138Smav		return (0);
783184138Smav
784184138Smav	if (BHNDB_DEBUG(PRIO))
785184138Smav		device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n",
786184138Smav		    type, rman_get_start(r), rman_get_size(r));
787184138Smav
788184138Smav	return (bhndb_try_activate_resource(sc, rman_get_device(r), type,
789184138Smav	    rman_get_rid(r), r, NULL));
790184138Smav}
791184138Smav
792184138Smav
793266200Sian/**
794184138Smav * Default bhndb(4) implementation of BUS_READ_IVAR().
795184138Smav */
796184138Smavstatic int
797184138Smavbhndb_read_ivar(device_t dev, device_t child, int index,
798184138Smav    uintptr_t *result)
799185661Smav{
800185661Smav	return (ENOENT);
801185661Smav}
802185661Smav
803185661Smav/**
804184138Smav * Default bhndb(4) implementation of BUS_WRITE_IVAR().
805266200Sian */
806184138Smavstatic int
807184138Smavbhndb_write_ivar(device_t dev, device_t child, int index,
808184138Smav    uintptr_t value)
809184138Smav{
810184138Smav	return (ENOENT);
811184138Smav}
812184138Smav
813184138Smav/**
814184138Smav * Return the address space for the given @p child device.
815184138Smav */
816266200Sianbhndb_addrspace
817266200Sianbhndb_get_addrspace(struct bhndb_softc *sc, device_t child)
818266200Sian{
819266200Sian	struct bhndb_devinfo	*dinfo;
820266200Sian	device_t		 imd_dev;
821266200Sian
822266200Sian	/* Find the directly attached parent of the requesting device */
823266200Sian	imd_dev = child;
824266200Sian	while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev)
825266200Sian		imd_dev = device_get_parent(imd_dev);
826266200Sian
827184138Smav	if (imd_dev == NULL)
828184138Smav		panic("bhndb address space request for non-child device %s\n",
829184138Smav		     device_get_nameunit(child));
830184138Smav
831184138Smav	dinfo = device_get_ivars(imd_dev);
832184138Smav	return (dinfo->addrspace);
833266200Sian}
834184138Smav
835184138Smav/**
836184138Smav * Return the rman instance for a given resource @p type, if any.
837184138Smav *
838184138Smav * @param sc The bhndb device state.
839184138Smav * @param child The requesting child.
840184138Smav * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...)
841184138Smav */
842184138Smavstatic struct rman *
843184138Smavbhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
844184138Smav{
845184138Smav	switch (bhndb_get_addrspace(sc, child)) {
846184138Smav	case BHNDB_ADDRSPACE_NATIVE:
847184138Smav		switch (type) {
848184138Smav		case SYS_RES_MEMORY:
849184138Smav			return (&sc->bus_res->ht_mem_rman);
850184138Smav		case SYS_RES_IRQ:
851184138Smav			return (NULL);
852184138Smav		default:
853184138Smav			return (NULL);
854184138Smav		};
855184138Smav
856184138Smav	case BHNDB_ADDRSPACE_BRIDGED:
857184138Smav		switch (type) {
858184138Smav		case SYS_RES_MEMORY:
859184138Smav			return (&sc->bus_res->br_mem_rman);
860184138Smav		case SYS_RES_IRQ:
861184138Smav			// TODO
862184138Smav			// return &sc->irq_rman;
863184138Smav			return (NULL);
864184138Smav		default:
865184138Smav			return (NULL);
866184138Smav		};
867184138Smav	}
868184138Smav
869184138Smav	/* Quieten gcc */
870184138Smav	return (NULL);
871184138Smav}
872184138Smav
873184138Smav/**
874241600Sgonzo * Default implementation of BUS_ADD_CHILD()
875266200Sian */
876266200Sianstatic device_t
877184138Smavbhndb_add_child(device_t dev, u_int order, const char *name, int unit)
878184138Smav{
879184138Smav	struct bhndb_devinfo	*dinfo;
880184138Smav	device_t		 child;
881184138Smav
882184138Smav	child = device_add_child_ordered(dev, order, name, unit);
883184138Smav	if (child == NULL)
884184138Smav		return (NULL);
885184138Smav
886184138Smav	dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT);
887184138Smav	if (dinfo == NULL) {
888184138Smav		device_delete_child(dev, child);
889184138Smav		return (NULL);
890184138Smav	}
891184138Smav
892184138Smav	dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
893184138Smav	resource_list_init(&dinfo->resources);
894184138Smav
895184138Smav	device_set_ivars(child, dinfo);
896184138Smav
897184138Smav	return (child);
898184138Smav}
899184138Smav
900184138Smav/**
901184138Smav * Default implementation of BUS_CHILD_DELETED().
902184138Smav */
903254496Sianstatic void
904254496Sianbhndb_child_deleted(device_t dev, device_t child)
905254496Sian{
906254496Sian	struct bhndb_devinfo *dinfo = device_get_ivars(child);
907254496Sian	if (dinfo != NULL) {
908254496Sian		resource_list_free(&dinfo->resources);
909254496Sian		free(dinfo, M_BHND);
910184138Smav	}
911184138Smav
912184138Smav	device_set_ivars(child, NULL);
913184138Smav}
914184138Smav
915184138Smav/**
916184138Smav * Default implementation of BHNDB_GET_CHIPID().
917184138Smav */
918184138Smavstatic const struct bhnd_chipid *
919184138Smavbhndb_get_chipid(device_t dev, device_t child)
920184138Smav{
921184138Smav	struct bhndb_softc *sc = device_get_softc(dev);
922184138Smav	return (&sc->chipid);
923184138Smav}
924184138Smav
925184138Smav
926184138Smav/**
927184138Smav * Default implementation of BHNDB_IS_HW_DISABLED().
928184138Smav */
929184138Smavstatic bool
930184138Smavbhndb_is_hw_disabled(device_t dev, device_t child) {
931184138Smav	struct bhndb_softc	*sc;
932184138Smav	struct bhnd_core_info	 core;
933184138Smav
934254423Sian	sc = device_get_softc(dev);
935184138Smav
936254423Sian	/* Requestor must be attached to the bhnd bus */
937254423Sian	if (device_get_parent(child) != sc->bus_dev) {
938254423Sian		return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child));
939254423Sian	}
940254423Sian
941254423Sian	/* Fetch core info */
942254423Sian	core = bhnd_get_core_info(child);
943254423Sian
944254423Sian	/* Try to defer to the bhndb bus parent */
945254423Sian	if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core))
946254423Sian		return (true);
947254423Sian
948254423Sian	/* Otherwise, we treat bridge-capable cores as unpopulated if they're
949184138Smav	 * not the configured host bridge */
950184138Smav	if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core)))
951184138Smav		return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child);
952184138Smav
953184138Smav	/* Otherwise, assume the core is populated */
954184138Smav	return (false);
955184138Smav}
956184138Smav
957184138Smav/* ascending core index comparison used by bhndb_find_hostb_device() */
958184138Smavstatic int
959241600Sgonzocompare_core_index(const void *lhs, const void *rhs)
960184138Smav{
961184138Smav	u_int left = bhnd_get_core_index(*(const device_t *) lhs);
962184138Smav	u_int right = bhnd_get_core_index(*(const device_t *) rhs);
963241600Sgonzo
964184138Smav	if (left < right)
965184138Smav		return (-1);
966184138Smav	else if (left > right)
967184138Smav		return (1);
968184138Smav	else
969254512Srpaulo		return (0);
970254512Srpaulo}
971184138Smav
972184138Smav/**
973254512Srpaulo * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE().
974254512Srpaulo *
975254512Srpaulo * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged
976254512Srpaulo * bhnd(4) devices to determine the hostb core:
977184138Smav *
978184138Smav * - The core must have a Broadcom vendor ID.
979184138Smav * - The core devclass must match the bridge type.
980184138Smav * - The core must be the first device on the bus with the bridged device
981184138Smav *   class.
982184138Smav *
983184138Smav * @param dev The bhndb device
984184138Smav * @param child The requesting bhnd bus.
985184138Smav */
986184138Smavstatic device_t
987184138Smavbhndb_find_hostb_device(device_t dev, device_t child)
988184138Smav{
989184138Smav	struct bhndb_softc	*sc;
990184138Smav	struct bhnd_core_match	 md;
991184138Smav	device_t		 hostb_dev, *devlist;
992184138Smav	int                      devcnt, error;
993184138Smav
994184138Smav	sc = device_get_softc(dev);
995184138Smav
996247495Sgonzo	/* Determine required device class and set up a match descriptor. */
997184138Smav	md = (struct bhnd_core_match) {
998184138Smav		.vendor = BHND_MFGID_BCM,
999184138Smav		.device = BHND_COREID_INVALID,
1000184138Smav		.hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID },
1001184138Smav		.class = sc->bridge_class,
1002254512Srpaulo		.unit = 0
1003184138Smav	};
1004184138Smav
1005184138Smav	/* Must be the absolute first matching device on the bus. */
1006184138Smav	if ((error = device_get_children(child, &devlist, &devcnt)))
1007184138Smav		return (false);
1008184138Smav
1009278688Sian	/* Sort by core index value, ascending */
1010184138Smav	qsort(devlist, devcnt, sizeof(*devlist), compare_core_index);
1011184138Smav
1012254512Srpaulo	/* Find the hostb device */
1013254512Srpaulo	hostb_dev = NULL;
1014184138Smav	for (int i = 0; i < devcnt; i++) {
1015184138Smav		if (bhnd_device_matches(devlist[i], &md)) {
1016184138Smav			hostb_dev = devlist[i];
1017254512Srpaulo			break;
1018254512Srpaulo		}
1019184138Smav	}
1020278688Sian
1021184138Smav	/* Clean up */
1022184138Smav	free(devlist, M_TEMP);
1023184138Smav
1024184138Smav	return (hostb_dev);
1025184138Smav}
1026184138Smav
1027184138Smav/**
1028184138Smav * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE().
1029184138Smav */
1030184138Smavstatic struct resource *
1031184138Smavbhndb_alloc_resource(device_t dev, device_t child, int type,
1032184138Smav    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
1033184138Smav{
1034184138Smav	struct bhndb_softc		*sc;
1035184138Smav	struct resource_list_entry	*rle;
1036184138Smav	struct resource			*rv;
1037184138Smav	struct rman			*rm;
1038184138Smav	int				 error;
1039184138Smav	bool				 passthrough, isdefault;
1040184138Smav
1041184138Smav	sc = device_get_softc(dev);
1042184138Smav	passthrough = (device_get_parent(child) != dev);
1043184138Smav	isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
1044184138Smav	rle = NULL;
1045184138Smav
1046184138Smav	/* Populate defaults */
1047184138Smav	if (!passthrough && isdefault) {
1048184138Smav		/* Fetch the resource list entry. */
1049184138Smav		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
1050184138Smav		    type, *rid);
1051184138Smav		if (rle == NULL) {
1052184138Smav			device_printf(dev,
1053184138Smav			    "default resource %#x type %d for child %s "
1054187876Smav			    "not found\n", *rid, type,
1055187876Smav			    device_get_nameunit(child));
1056187876Smav
1057241600Sgonzo			return (NULL);
1058184138Smav		}
1059184138Smav
1060184138Smav		if (rle->res != NULL) {
1061184138Smav			device_printf(dev,
1062266200Sian			    "resource entry %#x type %d for child %s is busy\n",
1063184138Smav			    *rid, type, device_get_nameunit(child));
1064184138Smav
1065241600Sgonzo			return (NULL);
1066241600Sgonzo		}
1067184138Smav
1068184138Smav		start = rle->start;
1069184138Smav		end = rle->end;
1070184138Smav		count = ulmax(count, rle->count);
1071184138Smav	}
1072184138Smav
1073184138Smav	/* Validate resource addresses */
1074184138Smav	if (start > end || count > ((end - start) + 1))
1075187876Smav		return (NULL);
1076187876Smav
1077187876Smav	/* Fetch the resource manager */
1078187876Smav	rm = bhndb_get_rman(sc, child, type);
1079187876Smav	if (rm == NULL)
1080187876Smav		return (NULL);
1081184138Smav
1082184138Smav	/* Make our reservation */
1083184138Smav	rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
1084184138Smav	    child);
1085188724Smav	if (rv == NULL)
1086188724Smav		return (NULL);
1087241600Sgonzo
1088188724Smav	rman_set_rid(rv, *rid);
1089188724Smav
1090188724Smav	/* Activate */
1091184138Smav	if (flags & RF_ACTIVE) {
1092184138Smav		error = bus_activate_resource(child, type, *rid, rv);
1093184138Smav		if (error) {
1094241600Sgonzo			device_printf(dev,
1095241600Sgonzo			    "failed to activate entry %#x type %d for "
1096184138Smav				"child %s: %d\n",
1097184138Smav			     *rid, type, device_get_nameunit(child), error);
1098184138Smav
1099184138Smav			rman_release_resource(rv);
1100184138Smav
1101184138Smav			return (NULL);
1102184138Smav		}
1103184138Smav	}
1104184138Smav
1105184138Smav	/* Update child's resource list entry */
1106241600Sgonzo	if (rle != NULL) {
1107241600Sgonzo		rle->res = rv;
1108184138Smav		rle->start = rman_get_start(rv);
1109184138Smav		rle->end = rman_get_end(rv);
1110184138Smav		rle->count = rman_get_size(rv);
1111184138Smav	}
1112184138Smav
1113184138Smav	return (rv);
1114185722Smav}
1115184138Smav
1116184138Smav/**
1117184138Smav * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE().
1118184138Smav */
1119184138Smavstatic int
1120184138Smavbhndb_release_resource(device_t dev, device_t child, int type, int rid,
1121184138Smav    struct resource *r)
1122241600Sgonzo{
1123241600Sgonzo	int error;
1124184138Smav
1125184138Smav	/* Deactivate resources */
1126184138Smav	if (rman_get_flags(r) & RF_ACTIVE) {
1127184138Smav		error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
1128184138Smav		if (error)
1129184138Smav			return (error);
1130184138Smav	}
1131185722Smav
1132184138Smav	if ((error = rman_release_resource(r)))
1133184138Smav		return (error);
1134184138Smav
1135184138Smav	return (0);
1136184138Smav}
1137184138Smav
1138184138Smav/**
1139184138Smav * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE().
1140184138Smav */
1141184138Smavstatic int
1142184138Smavbhndb_adjust_resource(device_t dev, device_t child, int type,
1143184138Smav    struct resource *r, rman_res_t start, rman_res_t end)
1144184138Smav{
1145184138Smav	struct bhndb_softc		*sc;
1146184138Smav	struct rman			*rm;
1147184138Smav	int				 error;
1148184138Smav
1149184138Smav	sc = device_get_softc(dev);
1150184138Smav	error = 0;
1151184138Smav
1152184138Smav	/* Fetch resource manager */
1153184138Smav	rm = bhndb_get_rman(sc, child, type);
1154184138Smav	if (rm == NULL)
1155184138Smav		return (ENXIO);
1156184138Smav
1157184138Smav	if (!rman_is_region_manager(r, rm))
1158184138Smav		return (ENXIO);
1159184138Smav
1160184138Smav	/* If active, adjustment is limited by the assigned window. */
1161184138Smav	BHNDB_LOCK(sc);
1162184138Smav
1163184138Smav	// TODO: Currently unsupported
1164184138Smav	error = ENODEV;
1165184138Smav
1166184138Smav	BHNDB_UNLOCK(sc);
1167184138Smav	if (!error)
1168184138Smav		error = rman_adjust_resource(r, start, end);
1169184138Smav
1170184138Smav	return (error);
1171184138Smav}
1172184138Smav
1173184138Smav/**
1174184138Smav * Initialize child resource @p r with a virtual address, tag, and handle
1175184138Smav * copied from @p parent, adjusted to contain only the range defined by
1176246891Sgonzo * @p offsize and @p size.
1177184138Smav *
1178184138Smav * @param r The register to be initialized.
1179184138Smav * @param parent The parent bus resource that fully contains the subregion.
1180184138Smav * @param offset The subregion offset within @p parent.
1181184138Smav * @param size The subregion size.
1182184138Smav * @p r.
1183184138Smav */
1184184138Smavstatic int
1185184138Smavbhndb_init_child_resource(struct resource *r,
1186184138Smav    struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
1187184138Smav{
1188278688Sian	bus_space_handle_t	bh, child_bh;
1189184138Smav	bus_space_tag_t		bt;
1190184138Smav	uintptr_t		vaddr;
1191184138Smav	int			error;
1192247495Sgonzo
1193247495Sgonzo	/* Fetch the parent resource's real bus values */
1194247495Sgonzo	vaddr = (uintptr_t) rman_get_virtual(parent);
1195247495Sgonzo	bt = rman_get_bustag(parent);
1196247495Sgonzo	bh = rman_get_bushandle(parent);
1197247495Sgonzo
1198247495Sgonzo	/* Configure child resource with window-adjusted real bus values */
1199247495Sgonzo	vaddr += offset;
1200184138Smav	error = bus_space_subregion(bt, bh, offset, size, &child_bh);
1201184138Smav	if (error)
1202184138Smav		return (error);
1203184138Smav
1204184138Smav	rman_set_virtual(r, (void *) vaddr);
1205184138Smav	rman_set_bustag(r, bt);
1206184138Smav	rman_set_bushandle(r, child_bh);
1207184138Smav
1208184138Smav	return (0);
1209184138Smav}
1210184138Smav
1211184138Smav/**
1212184138Smav * Attempt activation of a fixed register window mapping for @p child.
1213184138Smav *
1214184138Smav * @param sc BHNDB device state.
1215184138Smav * @param region The static region definition capable of mapping @p r.
1216184138Smav * @param child A child requesting resource activation.
1217184138Smav * @param type Resource type.
1218184138Smav * @param rid Resource identifier.
1219184138Smav * @param r Resource to be activated.
1220184138Smav *
1221184138Smav * @retval 0 if @p r was activated successfully
1222184138Smav * @retval ENOENT if no fixed register window was found.
1223184138Smav * @retval non-zero if @p r could not be activated.
1224184138Smav */
1225184138Smavstatic int
1226184138Smavbhndb_activate_static_region(struct bhndb_softc *sc,
1227184138Smav    struct bhndb_region *region, device_t child, int type, int rid,
1228184138Smav    struct resource *r)
1229184138Smav{
1230184138Smav	struct resource			*bridge_res;
1231184138Smav	const struct bhndb_regwin	*win;
1232184138Smav	bhnd_size_t			 parent_offset;
1233184138Smav	rman_res_t			 r_start, r_size;
1234184138Smav	int				 error;
1235184138Smav
1236184138Smav	win = region->static_regwin;
1237184138Smav
1238247495Sgonzo	KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type),
1239247495Sgonzo	    ("can't activate non-static region"));
1240247495Sgonzo
1241247495Sgonzo	r_start = rman_get_start(r);
1242247495Sgonzo	r_size = rman_get_size(r);
1243247495Sgonzo
1244247495Sgonzo	/* Find the corresponding bridge resource */
1245278688Sian	bridge_res = bhndb_find_regwin_resource(sc->bus_res, win);
1246278688Sian	if (bridge_res == NULL)
1247278688Sian		return (ENXIO);
1248278688Sian
1249278688Sian	/* Calculate subregion offset within the parent resource */
1250278688Sian	parent_offset = r_start - region->addr;
1251278688Sian	parent_offset += win->win_offset;
1252278688Sian
1253278688Sian	/* Configure resource with its real bus values. */
1254184138Smav	error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size);
1255184138Smav	if (error)
1256184138Smav		return (error);
1257184138Smav
1258184138Smav	/* Mark active */
1259184138Smav	if ((error = rman_activate_resource(r)))
1260184138Smav		return (error);
1261184138Smav
1262184138Smav	return (0);
1263184138Smav}
1264184138Smav
1265184138Smav/**
1266184138Smav * Attempt to allocate/retain a dynamic register window for @p r, returning
1267184138Smav * the retained window.
1268184138Smav *
1269184138Smav * @param sc The bhndb driver state.
1270184138Smav * @param r The resource for which a window will be retained.
1271184138Smav */
1272241600Sgonzostatic struct bhndb_dw_alloc *
1273241600Sgonzobhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r)
1274184138Smav{
1275241600Sgonzo	struct bhndb_dw_alloc	*dwa;
1276241600Sgonzo	rman_res_t		 r_start, r_size;
1277241600Sgonzo	int			 error;
1278241600Sgonzo
1279241600Sgonzo	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1280241600Sgonzo
1281241600Sgonzo	r_start = rman_get_start(r);
1282241600Sgonzo	r_size = rman_get_size(r);
1283241600Sgonzo
1284241600Sgonzo	/* Look for an existing dynamic window we can reference */
1285241600Sgonzo	dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size);
1286184138Smav	if (dwa != NULL) {
1287241600Sgonzo		if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0)
1288241600Sgonzo			return (dwa);
1289241600Sgonzo
1290241600Sgonzo		return (NULL);
1291187876Smav	}
1292241600Sgonzo
1293241600Sgonzo	/* Otherwise, try to reserve a free window */
1294241600Sgonzo	dwa = bhndb_dw_next_free(sc->bus_res);
1295241600Sgonzo	if (dwa == NULL) {
1296241600Sgonzo		/* No free windows */
1297241600Sgonzo		return (NULL);
1298184138Smav	}
1299241600Sgonzo
1300241600Sgonzo	/* Set the window target */
1301241600Sgonzo	error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r),
1302241600Sgonzo	    rman_get_size(r));
1303241600Sgonzo	if (error) {
1304184138Smav		device_printf(sc->dev, "dynamic window initialization "
1305241600Sgonzo			"for 0x%llx-0x%llx failed: %d\n",
1306184138Smav			(unsigned long long) r_start,
1307241600Sgonzo			(unsigned long long) r_start + r_size - 1,
1308241600Sgonzo			error);
1309241600Sgonzo		return (NULL);
1310241600Sgonzo	}
1311241600Sgonzo
1312241600Sgonzo	/* Add our reservation */
1313241600Sgonzo	if (bhndb_dw_retain(sc->bus_res, dwa, r))
1314241600Sgonzo		return (NULL);
1315276287Sian
1316276287Sian	return (dwa);
1317276287Sian}
1318241600Sgonzo
1319241600Sgonzo/**
1320241600Sgonzo * Activate a resource using any viable static or dynamic register window.
1321241600Sgonzo *
1322241600Sgonzo * @param sc The bhndb driver state.
1323241600Sgonzo * @param child The child holding ownership of @p r.
1324241600Sgonzo * @param type The type of the resource to be activated.
1325241600Sgonzo * @param rid The resource ID of @p r.
1326241600Sgonzo * @param r The resource to be activated
1327241600Sgonzo * @param[out] indirect On error and if not NULL, will be set to 'true' if
1328241600Sgonzo * the caller should instead use an indirect resource mapping.
1329241600Sgonzo *
1330241600Sgonzo * @retval 0 success
1331241600Sgonzo * @retval non-zero activation failed.
1332241600Sgonzo */
1333241600Sgonzostatic int
1334241600Sgonzobhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
1335241600Sgonzo    int rid, struct resource *r, bool *indirect)
1336241600Sgonzo{
1337241600Sgonzo	struct bhndb_region	*region;
1338241600Sgonzo	struct bhndb_dw_alloc	*dwa;
1339241600Sgonzo	bhndb_priority_t	 dw_priority;
1340241600Sgonzo	rman_res_t		 r_start, r_size;
1341241600Sgonzo	rman_res_t		 parent_offset;
1342241600Sgonzo	int			 error;
1343184138Smav
1344184138Smav	BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
1345241600Sgonzo
1346241600Sgonzo	// TODO - IRQs
1347184138Smav	if (type != SYS_RES_MEMORY)
1348184138Smav		return (ENXIO);
1349184138Smav
1350184138Smav	if (indirect)
1351184138Smav		*indirect = false;
1352184138Smav
1353184138Smav	r_start = rman_get_start(r);
1354222475Sjchandra	r_size = rman_get_size(r);
1355184138Smav
1356184138Smav	/* Activate native addrspace resources using the host address space */
1357222475Sjchandra	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) {
1358184138Smav		struct resource *parent;
1359184138Smav
1360222475Sjchandra		/* Find the bridge resource referenced by the child */
1361184138Smav		parent = bhndb_find_resource_range(sc->bus_res, r_start,
1362184138Smav		    r_size);
1363222475Sjchandra		if (parent == NULL) {
1364184138Smav			device_printf(sc->dev, "host resource not found "
1365184138Smav			     "for 0x%llx-0x%llx\n",
1366222475Sjchandra			     (unsigned long long) r_start,
1367184138Smav			     (unsigned long long) r_start + r_size - 1);
1368184138Smav			return (ENOENT);
1369222475Sjchandra		}
1370184138Smav
1371184138Smav		/* Initialize child resource with the real bus values */
1372222475Sjchandra		error = bhndb_init_child_resource(r, parent,
1373184138Smav		    r_start - rman_get_start(parent), r_size);
1374184138Smav		if (error)
1375222475Sjchandra			return (error);
1376184138Smav
1377184138Smav		/* Try to activate child resource */
1378222475Sjchandra		return (rman_activate_resource(r));
1379184138Smav	}
1380184138Smav
1381222475Sjchandra	/* Default to low priority */
1382184138Smav	dw_priority = BHNDB_PRIORITY_LOW;
1383184138Smav
1384222475Sjchandra	/* Look for a bus region matching the resource's address range */
1385184138Smav	region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
1386184138Smav	if (region != NULL)
1387222475Sjchandra		dw_priority = region->priority;
1388184138Smav
1389184138Smav	/* Prefer static mappings over consuming a dynamic windows. */
1390222475Sjchandra	if (region && region->static_regwin) {
1391184138Smav		error = bhndb_activate_static_region(sc, region, child, type,
1392184452Smav		    rid, r);
1393222475Sjchandra		if (error)
1394184452Smav			device_printf(sc->dev, "static window allocation "
1395184138Smav			     "for 0x%llx-0x%llx failed\n",
1396184138Smav			     (unsigned long long) r_start,
1397184138Smav			     (unsigned long long) r_start + r_size - 1);
1398184138Smav		return (error);
1399241600Sgonzo	}
1400241600Sgonzo
1401184138Smav	/* A dynamic window will be required; is this resource high enough
1402184138Smav	 * priority to be reserved a dynamic window? */
1403184138Smav	if (dw_priority < sc->bus_res->min_prio) {
1404184138Smav		if (indirect)
1405184138Smav			*indirect = true;
1406184138Smav
1407184138Smav		return (ENOMEM);
1408184138Smav	}
1409184138Smav
1410184138Smav	/* Find and retain a usable window */
1411184138Smav	BHNDB_LOCK(sc); {
1412184138Smav		dwa = bhndb_retain_dynamic_window(sc, r);
1413184138Smav	} BHNDB_UNLOCK(sc);
1414184138Smav
1415184138Smav	if (dwa == NULL) {
1416184138Smav		if (indirect)
1417184138Smav			*indirect = true;
1418246886Sgonzo		return (ENOMEM);
1419246886Sgonzo	}
1420184138Smav
1421184138Smav	/* Configure resource with its real bus values. */
1422246886Sgonzo	parent_offset = dwa->win->win_offset;
1423246886Sgonzo	parent_offset += r_start - dwa->target;
1424246886Sgonzo
1425246886Sgonzo	error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset,
1426246886Sgonzo	    dwa->win->win_size);
1427246886Sgonzo	if (error)
1428246886Sgonzo		goto failed;
1429246886Sgonzo
1430246886Sgonzo	/* Mark active */
1431246886Sgonzo	if ((error = rman_activate_resource(r)))
1432184138Smav		goto failed;
1433246886Sgonzo
1434246886Sgonzo	return (0);
1435246886Sgonzo
1436246886Sgonzofailed:
1437246886Sgonzo	/* Release our region allocation. */
1438246886Sgonzo	BHNDB_LOCK(sc);
1439246886Sgonzo	bhndb_dw_release(sc->bus_res, dwa, r);
1440246886Sgonzo	BHNDB_UNLOCK(sc);
1441246886Sgonzo
1442184138Smav	return (error);
1443184138Smav}
1444184138Smav
1445184138Smav/**
1446184138Smav * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE().
1447184138Smav *
1448184138Smav * Maps resource activation requests to a viable static or dynamic
1449184138Smav * register window, if any.
1450184138Smav */
1451184138Smavstatic int
1452184138Smavbhndb_activate_resource(device_t dev, device_t child, int type, int rid,
1453184138Smav    struct resource *r)
1454184138Smav{
1455184138Smav	struct bhndb_softc *sc = device_get_softc(dev);
1456184138Smav
1457184138Smav	return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL));
1458184138Smav}
1459184138Smav
1460184138Smav/**
1461184138Smav * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE().
1462184138Smav */
1463184138Smavstatic int
1464184138Smavbhndb_deactivate_resource(device_t dev, device_t child, int type,
1465184452Smav    int rid, struct resource *r)
1466184138Smav{
1467184138Smav	struct bhndb_dw_alloc	*dwa;
1468184138Smav	struct bhndb_softc	*sc;
1469184138Smav	struct rman		*rm;
1470184138Smav	int			 error;
1471241600Sgonzo
1472	sc = device_get_softc(dev);
1473
1474	if ((rm = bhndb_get_rman(sc, child, type)) == NULL)
1475		return (EINVAL);
1476
1477	/* Mark inactive */
1478	if ((error = rman_deactivate_resource(r)))
1479		return (error);
1480
1481	/* Free any dynamic window allocation. */
1482	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1483		BHNDB_LOCK(sc);
1484		dwa = bhndb_dw_find_resource(sc->bus_res, r);
1485		if (dwa != NULL)
1486			bhndb_dw_release(sc->bus_res, dwa, r);
1487		BHNDB_UNLOCK(sc);
1488	}
1489
1490	return (0);
1491}
1492
1493/**
1494 * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST().
1495 */
1496static struct resource_list *
1497bhndb_get_resource_list(device_t dev, device_t child)
1498{
1499	struct bhndb_devinfo *dinfo = device_get_ivars(child);
1500	return (&dinfo->resources);
1501}
1502
1503/**
1504 * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE().
1505 *
1506 * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to
1507 * be activated by the bridge.
1508 *
1509 * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register
1510 * window, a dynamic register window, or configures @p r as an indirect
1511 * resource -- in that order.
1512 */
1513static int
1514bhndb_activate_bhnd_resource(device_t dev, device_t child,
1515    int type, int rid, struct bhnd_resource *r)
1516{
1517	struct bhndb_softc	*sc;
1518	struct bhndb_region	*region;
1519	rman_res_t		 r_start, r_size;
1520	int 			 error;
1521	bool			 indirect;
1522
1523	KASSERT(!r->direct,
1524	    ("direct flag set on inactive resource"));
1525
1526	KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE),
1527	    ("RF_ACTIVE set on inactive resource"));
1528
1529	sc = device_get_softc(dev);
1530
1531	r_start = rman_get_start(r->res);
1532	r_size = rman_get_size(r->res);
1533
1534	/* Verify bridged address range's resource priority, and skip direct
1535	 * allocation if the priority is too low. */
1536	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1537		bhndb_priority_t r_prio;
1538
1539		region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
1540		if (region != NULL)
1541			r_prio = region->priority;
1542		else
1543			r_prio = BHNDB_PRIORITY_NONE;
1544
1545		/* If less than the minimum dynamic window priority, this
1546		 * resource should always be indirect. */
1547		if (r_prio < sc->bus_res->min_prio)
1548			return (0);
1549	}
1550
1551	/* Attempt direct activation */
1552	error = bhndb_try_activate_resource(sc, child, type, rid, r->res,
1553	    &indirect);
1554	if (!error) {
1555		r->direct = true;
1556	} else if (indirect) {
1557		/* The request was valid, but no viable register window is
1558		 * available; indirection must be employed. */
1559		error = 0;
1560		r->direct = false;
1561	}
1562
1563	if (BHNDB_DEBUG(PRIO) &&
1564	    bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED)
1565	{
1566		device_printf(child, "activated 0x%llx-0x%llx as %s "
1567		    "resource\n",
1568		    (unsigned long long) r_start,
1569		    (unsigned long long) r_start + r_size - 1,
1570		    r->direct ? "direct" : "indirect");
1571	}
1572
1573	return (error);
1574};
1575
1576/**
1577 * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE().
1578 */
1579static int
1580bhndb_deactivate_bhnd_resource(device_t dev, device_t child,
1581    int type, int rid, struct bhnd_resource *r)
1582{
1583	int error;
1584
1585	/* Indirect resources don't require activation */
1586	if (!r->direct)
1587		return (0);
1588
1589	KASSERT(rman_get_flags(r->res) & RF_ACTIVE,
1590	    ("RF_ACTIVE not set on direct resource"));
1591
1592	/* Perform deactivation */
1593	error = bus_deactivate_resource(child, type, rid, r->res);
1594	if (!error)
1595		r->direct = false;
1596
1597	return (error);
1598};
1599
1600/**
1601 * Slow path for bhndb_io_resource().
1602 *
1603 * Iterates over the existing allocated dynamic windows looking for a viable
1604 * in-use region; the first matching region is returned.
1605 */
1606static struct bhndb_dw_alloc *
1607bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
1608    bus_size_t size, bus_size_t *offset)
1609{
1610	struct bhndb_resources	*br;
1611	struct bhndb_dw_alloc	*dwa;
1612
1613	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1614
1615	br = sc->bus_res;
1616
1617	/* Search for an existing dynamic mapping of this address range.
1618	 * Static regions are not searched, as a statically mapped
1619	 * region would never be allocated as an indirect resource. */
1620	for (size_t i = 0; i < br->dwa_count; i++) {
1621		const struct bhndb_regwin *win;
1622
1623		dwa = &br->dw_alloc[i];
1624		win = dwa->win;
1625
1626		KASSERT(win->win_type == BHNDB_REGWIN_T_DYN,
1627			("invalid register window type"));
1628
1629		/* Verify the range */
1630		if (addr < dwa->target)
1631			continue;
1632
1633		if (addr + size > dwa->target + win->win_size)
1634			continue;
1635
1636		/* Found */
1637		*offset = dwa->win->win_offset;
1638		*offset += addr - dwa->target;
1639
1640		return (dwa);
1641	}
1642
1643	/* not found */
1644	return (NULL);
1645}
1646
1647/**
1648 * Find the bridge resource to be used for I/O requests.
1649 *
1650 * @param sc Bridge driver state.
1651 * @param addr The I/O target address.
1652 * @param size The size of the I/O operation to be performed at @p addr.
1653 * @param[out] offset The offset within the returned resource at which
1654 * to perform the I/O request.
1655 */
1656static inline struct bhndb_dw_alloc *
1657bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
1658    bus_size_t *offset)
1659{
1660	struct bhndb_resources	*br;
1661	struct bhndb_dw_alloc	*dwa;
1662	int			 error;
1663
1664	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1665
1666	br = sc->bus_res;
1667
1668	/* Try to fetch a free window */
1669	dwa = bhndb_dw_next_free(br);
1670
1671	/*
1672	 * If no dynamic windows are available, look for an existing
1673	 * region that maps the target range.
1674	 *
1675	 * If none are found, this is a child driver bug -- our window
1676	 * over-commit should only fail in the case where a child driver leaks
1677	 * resources, or perform operations out-of-order.
1678	 *
1679	 * Broadcom HND chipsets are designed to not require register window
1680	 * swapping during execution; as long as the child devices are
1681	 * attached/detached correctly, using the hardware's required order
1682	 * of operations, there should always be a window available for the
1683	 * current operation.
1684	 */
1685	if (dwa == NULL) {
1686		dwa = bhndb_io_resource_slow(sc, addr, size, offset);
1687		if (dwa == NULL) {
1688			panic("register windows exhausted attempting to map "
1689			    "0x%llx-0x%llx\n",
1690			    (unsigned long long) addr,
1691			    (unsigned long long) addr+size-1);
1692		}
1693
1694		return (dwa);
1695	}
1696
1697	/* Adjust the window if the I/O request won't fit in the current
1698	 * target range. */
1699	if (addr < dwa->target ||
1700	   (dwa->target + dwa->win->win_size) - addr < size)
1701	{
1702		error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr,
1703		    size);
1704		if (error) {
1705		    panic("failed to set register window target mapping "
1706			    "0x%llx-0x%llx\n",
1707			    (unsigned long long) addr,
1708			    (unsigned long long) addr+size-1);
1709		}
1710	}
1711
1712	/* Calculate the offset and return */
1713	*offset = (addr - dwa->target) + dwa->win->win_offset;
1714	return (dwa);
1715}
1716
1717/*
1718 * BHND_BUS_(READ|WRITE_* implementations
1719 */
1720
1721/* bhndb_bus_(read|write) common implementation */
1722#define	BHNDB_IO_COMMON_SETUP(_io_size)				\
1723	struct bhndb_softc	*sc;				\
1724	struct bhndb_dw_alloc	*dwa;				\
1725	struct resource		*io_res;			\
1726	bus_size_t		 io_offset;			\
1727								\
1728	sc = device_get_softc(dev);				\
1729								\
1730	BHNDB_LOCK(sc);						\
1731	dwa = bhndb_io_resource(sc, rman_get_start(r->res) +	\
1732	    offset, _io_size, &io_offset);			\
1733	io_res = dwa->parent_res;				\
1734								\
1735	KASSERT(!r->direct,					\
1736	    ("bhnd_bus slow path used for direct resource"));	\
1737								\
1738	KASSERT(rman_get_flags(io_res) & RF_ACTIVE,		\
1739	    ("i/o resource is not active"));
1740
1741#define	BHNDB_IO_COMMON_TEARDOWN()				\
1742	BHNDB_UNLOCK(sc);
1743
1744/* Defines a bhndb_bus_read_* method implementation */
1745#define	BHNDB_IO_READ(_type, _name)				\
1746static _type							\
1747bhndb_bus_read_ ## _name (device_t dev, device_t child,		\
1748    struct bhnd_resource *r, bus_size_t offset)			\
1749{								\
1750	_type v;						\
1751	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1752	v = bus_read_ ## _name (io_res, io_offset);		\
1753	BHNDB_IO_COMMON_TEARDOWN();				\
1754								\
1755	return (v);						\
1756}
1757
1758/* Defines a bhndb_bus_write_* method implementation */
1759#define	BHNDB_IO_WRITE(_type, _name)				\
1760static void							\
1761bhndb_bus_write_ ## _name (device_t dev, device_t child,	\
1762    struct bhnd_resource *r, bus_size_t offset, _type value)	\
1763{								\
1764	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1765	bus_write_ ## _name (io_res, io_offset, value);		\
1766	BHNDB_IO_COMMON_TEARDOWN();				\
1767}
1768
1769/* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */
1770#define	BHNDB_IO_MISC(_type, _ptr, _op, _size)			\
1771static void							\
1772bhndb_bus_ ## _op ## _ ## _size (device_t dev,			\
1773    device_t child, struct bhnd_resource *r, bus_size_t offset,	\
1774    _type _ptr datap, bus_size_t count)				\
1775{								\
1776	BHNDB_IO_COMMON_SETUP(sizeof(_type) * count);		\
1777	bus_ ## _op ## _ ## _size (io_res, io_offset,		\
1778	    datap, count);					\
1779	BHNDB_IO_COMMON_TEARDOWN();				\
1780}
1781
1782/* Defines a complete set of read/write methods */
1783#define	BHNDB_IO_METHODS(_type, _size)				\
1784	BHNDB_IO_READ(_type, _size)				\
1785	BHNDB_IO_WRITE(_type, _size)				\
1786								\
1787	BHNDB_IO_READ(_type, stream_ ## _size)			\
1788	BHNDB_IO_WRITE(_type, stream_ ## _size)			\
1789								\
1790	BHNDB_IO_MISC(_type, *, read_multi, _size)		\
1791	BHNDB_IO_MISC(_type, *, write_multi, _size)		\
1792								\
1793	BHNDB_IO_MISC(_type, *, read_multi_stream, _size)	\
1794	BHNDB_IO_MISC(_type, *, write_multi_stream, _size)	\
1795								\
1796	BHNDB_IO_MISC(_type,  , set_multi, _size)		\
1797	BHNDB_IO_MISC(_type,  , set_region, _size)		\
1798	BHNDB_IO_MISC(_type, *, read_region, _size)		\
1799	BHNDB_IO_MISC(_type, *, write_region, _size)		\
1800								\
1801	BHNDB_IO_MISC(_type, *, read_region_stream, _size)	\
1802	BHNDB_IO_MISC(_type, *, write_region_stream, _size)
1803
1804BHNDB_IO_METHODS(uint8_t, 1);
1805BHNDB_IO_METHODS(uint16_t, 2);
1806BHNDB_IO_METHODS(uint32_t, 4);
1807
1808/**
1809 * Default bhndb(4) implementation of BHND_BUS_BARRIER().
1810 */
1811static void
1812bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
1813    bus_size_t offset, bus_size_t length, int flags)
1814{
1815	BHNDB_IO_COMMON_SETUP(length);
1816
1817	bus_barrier(io_res, io_offset + offset, length, flags);
1818
1819	BHNDB_IO_COMMON_TEARDOWN();
1820}
1821
1822/**
1823 * Default bhndb(4) implementation of BUS_SETUP_INTR().
1824 */
1825static int
1826bhndb_setup_intr(device_t dev, device_t child, struct resource *r,
1827    int flags, driver_filter_t filter, driver_intr_t handler, void *arg,
1828    void **cookiep)
1829{
1830	// TODO
1831	return (EOPNOTSUPP);
1832}
1833
1834/**
1835 * Default bhndb(4) implementation of BUS_TEARDOWN_INTR().
1836 */
1837static int
1838bhndb_teardown_intr(device_t dev, device_t child, struct resource *r,
1839    void *cookie)
1840{
1841	// TODO
1842	return (EOPNOTSUPP);
1843}
1844
1845/**
1846 * Default bhndb(4) implementation of BUS_CONFIG_INTR().
1847 */
1848static int
1849bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig,
1850    enum intr_polarity pol)
1851{
1852	// TODO
1853	return (EOPNOTSUPP);
1854}
1855
1856/**
1857 * Default bhndb(4) implementation of BUS_BIND_INTR().
1858 */
1859static int
1860bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu)
1861{
1862	// TODO
1863	return (EOPNOTSUPP);
1864}
1865
1866/**
1867 * Default bhndb(4) implementation of BUS_DESCRIBE_INTR().
1868 */
1869static int
1870bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie,
1871    const char *descr)
1872{
1873	// TODO
1874	return (EOPNOTSUPP);
1875}
1876
1877/**
1878 * Default bhndb(4) implementation of BUS_GET_DMA_TAG().
1879 */
1880static bus_dma_tag_t
1881bhndb_get_dma_tag(device_t dev, device_t child)
1882{
1883	// TODO
1884	return (NULL);
1885}
1886
1887static device_method_t bhndb_methods[] = {
1888	/* Device interface */ \
1889	DEVMETHOD(device_probe,			bhndb_generic_probe),
1890	DEVMETHOD(device_detach,		bhndb_generic_detach),
1891	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
1892	DEVMETHOD(device_suspend,		bhndb_generic_suspend),
1893	DEVMETHOD(device_resume,		bhndb_generic_resume),
1894
1895	/* Bus interface */
1896	DEVMETHOD(bus_probe_nomatch,		bhndb_probe_nomatch),
1897	DEVMETHOD(bus_print_child,		bhndb_print_child),
1898	DEVMETHOD(bus_child_pnpinfo_str,	bhndb_child_pnpinfo_str),
1899	DEVMETHOD(bus_child_location_str,	bhndb_child_location_str),
1900	DEVMETHOD(bus_add_child,		bhndb_add_child),
1901	DEVMETHOD(bus_child_deleted,		bhndb_child_deleted),
1902
1903	DEVMETHOD(bus_alloc_resource,		bhndb_alloc_resource),
1904	DEVMETHOD(bus_release_resource,		bhndb_release_resource),
1905	DEVMETHOD(bus_activate_resource,	bhndb_activate_resource),
1906	DEVMETHOD(bus_deactivate_resource,	bhndb_deactivate_resource),
1907
1908	DEVMETHOD(bus_setup_intr,		bhndb_setup_intr),
1909	DEVMETHOD(bus_teardown_intr,		bhndb_teardown_intr),
1910	DEVMETHOD(bus_config_intr,		bhndb_config_intr),
1911	DEVMETHOD(bus_bind_intr,		bhndb_bind_intr),
1912	DEVMETHOD(bus_describe_intr,		bhndb_describe_intr),
1913
1914	DEVMETHOD(bus_get_dma_tag,		bhndb_get_dma_tag),
1915
1916	DEVMETHOD(bus_adjust_resource,		bhndb_adjust_resource),
1917	DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
1918	DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
1919	DEVMETHOD(bus_delete_resource,		bus_generic_rl_delete_resource),
1920	DEVMETHOD(bus_get_resource_list,	bhndb_get_resource_list),
1921
1922	DEVMETHOD(bus_read_ivar,		bhndb_read_ivar),
1923	DEVMETHOD(bus_write_ivar,		bhndb_write_ivar),
1924
1925	/* BHNDB interface */
1926	DEVMETHOD(bhndb_get_chipid,		bhndb_get_chipid),
1927	DEVMETHOD(bhndb_init_full_config,	bhndb_generic_init_full_config),
1928	DEVMETHOD(bhndb_find_hostb_device,	bhndb_find_hostb_device),
1929	DEVMETHOD(bhndb_suspend_resource,	bhndb_suspend_resource),
1930	DEVMETHOD(bhndb_resume_resource,	bhndb_resume_resource),
1931
1932	/* BHND interface */
1933	DEVMETHOD(bhnd_bus_is_hw_disabled,	bhndb_is_hw_disabled),
1934	DEVMETHOD(bhnd_bus_get_chipid,		bhndb_get_chipid),
1935	DEVMETHOD(bhnd_bus_activate_resource,	bhndb_activate_bhnd_resource),
1936	DEVMETHOD(bhnd_bus_deactivate_resource,	bhndb_deactivate_bhnd_resource),
1937	DEVMETHOD(bhnd_bus_get_nvram_var,	bhnd_bus_generic_get_nvram_var),
1938	DEVMETHOD(bhnd_bus_read_1,		bhndb_bus_read_1),
1939	DEVMETHOD(bhnd_bus_read_2,		bhndb_bus_read_2),
1940	DEVMETHOD(bhnd_bus_read_4,		bhndb_bus_read_4),
1941	DEVMETHOD(bhnd_bus_write_1,		bhndb_bus_write_1),
1942	DEVMETHOD(bhnd_bus_write_2,		bhndb_bus_write_2),
1943	DEVMETHOD(bhnd_bus_write_4,		bhndb_bus_write_4),
1944
1945	DEVMETHOD(bhnd_bus_read_stream_1,	bhndb_bus_read_stream_1),
1946	DEVMETHOD(bhnd_bus_read_stream_2,	bhndb_bus_read_stream_2),
1947	DEVMETHOD(bhnd_bus_read_stream_4,	bhndb_bus_read_stream_4),
1948	DEVMETHOD(bhnd_bus_write_stream_1,	bhndb_bus_write_stream_1),
1949	DEVMETHOD(bhnd_bus_write_stream_2,	bhndb_bus_write_stream_2),
1950	DEVMETHOD(bhnd_bus_write_stream_4,	bhndb_bus_write_stream_4),
1951
1952	DEVMETHOD(bhnd_bus_read_multi_1,	bhndb_bus_read_multi_1),
1953	DEVMETHOD(bhnd_bus_read_multi_2,	bhndb_bus_read_multi_2),
1954	DEVMETHOD(bhnd_bus_read_multi_4,	bhndb_bus_read_multi_4),
1955	DEVMETHOD(bhnd_bus_write_multi_1,	bhndb_bus_write_multi_1),
1956	DEVMETHOD(bhnd_bus_write_multi_2,	bhndb_bus_write_multi_2),
1957	DEVMETHOD(bhnd_bus_write_multi_4,	bhndb_bus_write_multi_4),
1958
1959	DEVMETHOD(bhnd_bus_read_multi_stream_1,	bhndb_bus_read_multi_stream_1),
1960	DEVMETHOD(bhnd_bus_read_multi_stream_2,	bhndb_bus_read_multi_stream_2),
1961	DEVMETHOD(bhnd_bus_read_multi_stream_4,	bhndb_bus_read_multi_stream_4),
1962	DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1),
1963	DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2),
1964	DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4),
1965
1966	DEVMETHOD(bhnd_bus_set_multi_1,		bhndb_bus_set_multi_1),
1967	DEVMETHOD(bhnd_bus_set_multi_2,		bhndb_bus_set_multi_2),
1968	DEVMETHOD(bhnd_bus_set_multi_4,		bhndb_bus_set_multi_4),
1969	DEVMETHOD(bhnd_bus_set_region_1,	bhndb_bus_set_region_1),
1970	DEVMETHOD(bhnd_bus_set_region_2,	bhndb_bus_set_region_2),
1971	DEVMETHOD(bhnd_bus_set_region_4,	bhndb_bus_set_region_4),
1972
1973	DEVMETHOD(bhnd_bus_read_region_1,	bhndb_bus_read_region_1),
1974	DEVMETHOD(bhnd_bus_read_region_2,	bhndb_bus_read_region_2),
1975	DEVMETHOD(bhnd_bus_read_region_4,	bhndb_bus_read_region_4),
1976	DEVMETHOD(bhnd_bus_write_region_1,	bhndb_bus_write_region_1),
1977	DEVMETHOD(bhnd_bus_write_region_2,	bhndb_bus_write_region_2),
1978	DEVMETHOD(bhnd_bus_write_region_4,	bhndb_bus_write_region_4),
1979
1980	DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1),
1981	DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2),
1982	DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4),
1983	DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1),
1984	DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2),
1985	DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4),
1986
1987	DEVMETHOD(bhnd_bus_barrier,		bhndb_bus_barrier),
1988
1989	DEVMETHOD_END
1990};
1991
1992devclass_t bhndb_devclass;
1993
1994DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc));
1995
1996MODULE_VERSION(bhndb, 1);
1997MODULE_DEPEND(bhndb, bhnd, 1, 1, 1);
1998MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1);
1999