xref: /freebsd-13-stable/sys/dev/bhnd/bhndb/bhndb.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33 
34 #include <sys/cdefs.h>
35 /*
36  * Abstract BHND Bridge Device Driver
37  *
38  * Provides generic support for bridging from a parent bus (such as PCI) to
39  * a BHND-compatible bus (e.g. bcma or siba).
40  */
41 
42 #include <sys/param.h>
43 #include <sys/kernel.h>
44 #include <sys/bus.h>
45 #include <sys/module.h>
46 #include <sys/systm.h>
47 
48 #include <machine/bus.h>
49 #include <sys/rman.h>
50 #include <machine/resource.h>
51 
52 #include <dev/bhnd/bhndvar.h>
53 #include <dev/bhnd/bhndreg.h>
54 
55 #include <dev/bhnd/bhnd_erom.h>
56 
57 #include <dev/bhnd/cores/chipc/chipcreg.h>
58 #include <dev/bhnd/nvram/bhnd_nvram.h>
59 
60 #include "bhnd_chipc_if.h"
61 #include "bhnd_nvram_if.h"
62 
63 #include "bhndbvar.h"
64 #include "bhndb_bus_if.h"
65 #include "bhndb_hwdata.h"
66 #include "bhndb_private.h"
67 
68 /* Debugging flags */
69 static u_long bhndb_debug = 0;
70 TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug);
71 
72 enum {
73 	BHNDB_DEBUG_PRIO = 1 << 0,
74 };
75 
76 #define	BHNDB_DEBUG(_type)	(BHNDB_DEBUG_ ## _type & bhndb_debug)
77 
78 static bool			 bhndb_hw_matches(struct bhndb_softc *sc,
79 				     struct bhnd_core_info *cores, u_int ncores,
80 				     const struct bhndb_hw *hw);
81 
82 static int			 bhndb_init_region_cfg(struct bhndb_softc *sc,
83 				     bhnd_erom_t *erom,
84 				     struct bhndb_resources *r,
85 				     struct bhnd_core_info *cores, u_int ncores,
86 				     const struct bhndb_hw_priority *table);
87 
88 static int			 bhndb_find_hwspec(struct bhndb_softc *sc,
89 				     struct bhnd_core_info *cores, u_int ncores,
90 				     const struct bhndb_hw **hw);
91 
92 bhndb_addrspace			 bhndb_get_addrspace(struct bhndb_softc *sc,
93 				     device_t child);
94 
95 static struct rman		*bhndb_get_rman(struct bhndb_softc *sc,
96 				     device_t child, int type);
97 
98 static int			 bhndb_init_child_resource(struct resource *r,
99 				     struct resource *parent,
100 				     bhnd_size_t offset,
101 				     bhnd_size_t size);
102 
103 static int			 bhndb_activate_static_region(
104 				     struct bhndb_softc *sc,
105 				     struct bhndb_region *region,
106 				     device_t child, int type, int rid,
107 				     struct resource *r);
108 
109 static int			 bhndb_try_activate_resource(
110 				     struct bhndb_softc *sc, device_t child,
111 				     int type, int rid, struct resource *r,
112 				     bool *indirect);
113 
114 static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc,
115 					bus_addr_t addr, bus_size_t size,
116 					bus_size_t *offset, bool *stolen,
117 					bus_addr_t *restore);
118 
119 /**
120  * Default bhndb(4) implementation of DEVICE_PROBE().
121  *
122  * This function provides the default bhndb implementation of DEVICE_PROBE(),
123  * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge().
124  */
125 int
bhndb_generic_probe(device_t dev)126 bhndb_generic_probe(device_t dev)
127 {
128 	return (BUS_PROBE_NOWILDCARD);
129 }
130 
131 static void
bhndb_probe_nomatch(device_t dev,device_t child)132 bhndb_probe_nomatch(device_t dev, device_t child)
133 {
134 	const char *name;
135 
136 	name = device_get_name(child);
137 	if (name == NULL)
138 		name = "unknown device";
139 
140 	device_printf(dev, "<%s> (no driver attached)\n", name);
141 }
142 
143 static int
bhndb_print_child(device_t dev,device_t child)144 bhndb_print_child(device_t dev, device_t child)
145 {
146 	struct resource_list	*rl;
147 	int			 retval = 0;
148 
149 	retval += bus_print_child_header(dev, child);
150 
151 	rl = BUS_GET_RESOURCE_LIST(dev, child);
152 	if (rl != NULL) {
153 		retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
154 		    "%#jx");
155 		retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ,
156 		    "%jd");
157 	}
158 
159 	retval += bus_print_child_domain(dev, child);
160 	retval += bus_print_child_footer(dev, child);
161 
162 	return (retval);
163 }
164 
165 static int
bhndb_child_pnpinfo_str(device_t bus,device_t child,char * buf,size_t buflen)166 bhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf,
167     size_t buflen)
168 {
169 	*buf = '\0';
170 	return (0);
171 }
172 
173 static int
bhndb_child_location_str(device_t dev,device_t child,char * buf,size_t buflen)174 bhndb_child_location_str(device_t dev, device_t child, char *buf,
175     size_t buflen)
176 {
177 	struct bhndb_softc *sc;
178 
179 	sc = device_get_softc(dev);
180 
181 	snprintf(buf, buflen, "base=0x%llx",
182 	    (unsigned long long) sc->chipid.enum_addr);
183 	return (0);
184 }
185 
186 /**
187  * Return true if @p cores matches the @p hw specification.
188  *
189  * @param sc BHNDB device state.
190  * @param cores A device table to match against.
191  * @param ncores The number of cores in @p cores.
192  * @param hw The hardware description to be matched against.
193  */
194 static bool
bhndb_hw_matches(struct bhndb_softc * sc,struct bhnd_core_info * cores,u_int ncores,const struct bhndb_hw * hw)195 bhndb_hw_matches(struct bhndb_softc *sc, struct bhnd_core_info *cores,
196     u_int ncores, const struct bhndb_hw *hw)
197 {
198 	for (u_int i = 0; i < hw->num_hw_reqs; i++) {
199 		const struct bhnd_core_match	*match;
200 		bool				 found;
201 
202 		match =  &hw->hw_reqs[i];
203 		found = false;
204 
205 		for (u_int d = 0; d < ncores; d++) {
206 			struct bhnd_core_info *core = &cores[d];
207 
208 			if (BHNDB_IS_CORE_DISABLED(sc->dev, sc->bus_dev, core))
209 				continue;
210 
211 			if (!bhnd_core_matches(core, match))
212 				continue;
213 
214 			found = true;
215 			break;
216 		}
217 
218 		if (!found)
219 			return (false);
220 	}
221 
222 	return (true);
223 }
224 
225 /**
226  * Initialize the region maps and priority configuration in @p br using
227  * the priority @p table and the set of cores enumerated by @p erom.
228  *
229  * @param sc The bhndb device state.
230  * @param br The resource state to be configured.
231  * @param erom EROM parser used to enumerate @p cores.
232  * @param cores All cores enumerated on the bridged bhnd bus.
233  * @param ncores The length of @p cores.
234  * @param table Hardware priority table to be used to determine the relative
235  * priorities of per-core port resources.
236  */
237 static int
bhndb_init_region_cfg(struct bhndb_softc * sc,bhnd_erom_t * erom,struct bhndb_resources * br,struct bhnd_core_info * cores,u_int ncores,const struct bhndb_hw_priority * table)238 bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom,
239     struct bhndb_resources *br, struct bhnd_core_info *cores, u_int ncores,
240     const struct bhndb_hw_priority *table)
241 {
242 	const struct bhndb_hw_priority	*hp;
243 	bhnd_addr_t			 addr;
244 	bhnd_size_t			 size;
245 	size_t				 prio_low, prio_default, prio_high;
246 	int				 error;
247 
248 	/* The number of port regions per priority band that must be accessible
249 	 * via dynamic register windows */
250 	prio_low = 0;
251 	prio_default = 0;
252 	prio_high = 0;
253 
254 	/*
255 	 * Register bridge regions covering all statically mapped ports.
256 	 */
257 	for (u_int i = 0; i < ncores; i++) {
258 		const struct bhndb_regwin	*regw;
259 		struct bhnd_core_info		*core;
260 		struct bhnd_core_match		 md;
261 
262 		core = &cores[i];
263 		md = bhnd_core_get_match_desc(core);
264 
265 		for (regw = br->cfg->register_windows;
266 		    regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
267 		{
268 			const struct bhndb_port_priority	*pp;
269 			uint32_t				 alloc_flags;
270 
271 			/* Only core windows are supported */
272 			if (regw->win_type != BHNDB_REGWIN_T_CORE)
273 				continue;
274 
275 			/* Skip non-matching cores. */
276 			if (!bhndb_regwin_match_core(regw, core))
277 				continue;
278 
279 			/* Fetch the base address of the mapped port */
280 			error = bhnd_erom_lookup_core_addr(erom, &md,
281 			    regw->d.core.port_type,
282 			    regw->d.core.port,
283 			    regw->d.core.region,
284 			    NULL,
285 			    &addr,
286 			    &size);
287 			if (error) {
288 				/* Skip non-applicable register windows */
289 				if (error == ENOENT)
290 					continue;
291 
292 				return (error);
293 			}
294 
295 			/*
296 			 * Apply the register window's region offset, if any.
297 			 */
298 			if (regw->d.core.offset > size) {
299 				device_printf(sc->dev, "invalid register "
300 				    "window offset %#jx for region %#jx+%#jx\n",
301 				    regw->d.core.offset, addr, size);
302 				return (EINVAL);
303 			}
304 
305 			addr += regw->d.core.offset;
306 
307 			/*
308 			 * Always defer to the register window's size.
309 			 *
310 			 * If the port size is smaller than the window size,
311 			 * this ensures that we fully utilize register windows
312 			 * larger than the referenced port.
313 			 *
314 			 * If the port size is larger than the window size, this
315 			 * ensures that we do not directly map the allocations
316 			 * within the region to a too-small window.
317 			 */
318 			size = regw->win_size;
319 
320 			/* Fetch allocation flags from the corresponding port
321 			 * priority entry, if any */
322 			pp = bhndb_hw_priorty_find_port(table, core,
323 			    regw->d.core.port_type, regw->d.core.port,
324 			    regw->d.core.region);
325 			if (pp != NULL) {
326 				alloc_flags = pp->alloc_flags;
327 			} else {
328 				alloc_flags = 0;
329 			}
330 
331 			/*
332 			 * Add to the bus region list.
333 			 *
334 			 * The window priority for a statically mapped region is
335 			 * always HIGH.
336 			 */
337 			error = bhndb_add_resource_region(br, addr, size,
338 			    BHNDB_PRIORITY_HIGH, alloc_flags, regw);
339 			if (error)
340 				return (error);
341 		}
342 	}
343 
344 	/*
345 	 * Perform priority accounting and register bridge regions for all
346 	 * ports defined in the priority table
347 	 */
348 	for (u_int i = 0; i < ncores; i++) {
349 		struct bhnd_core_info	*core;
350 		struct bhnd_core_match	 md;
351 
352 		core = &cores[i];
353 		md = bhnd_core_get_match_desc(core);
354 
355 		/*
356 		 * Skip priority accounting for cores that ...
357 		 */
358 
359 		/* ... do not require bridge resources */
360 		if (BHNDB_IS_CORE_DISABLED(sc->dev, sc->bus_dev, core))
361 			continue;
362 
363 		/* ... do not have a priority table entry */
364 		hp = bhndb_hw_priority_find_core(table, core);
365 		if (hp == NULL)
366 			continue;
367 
368 		/* ... are explicitly disabled in the priority table. */
369 		if (hp->priority == BHNDB_PRIORITY_NONE)
370 			continue;
371 
372 		/* Determine the number of dynamic windows required and
373 		 * register their bus_region entries. */
374 		for (u_int i = 0; i < hp->num_ports; i++) {
375 			const struct bhndb_port_priority *pp;
376 
377 			pp = &hp->ports[i];
378 
379 			/* Fetch the address+size of the mapped port. */
380 			error = bhnd_erom_lookup_core_addr(erom, &md,
381 			    pp->type, pp->port, pp->region,
382 			    NULL, &addr, &size);
383 			if (error) {
384 				/* Skip ports not defined on this device */
385 				if (error == ENOENT)
386 					continue;
387 
388 				return (error);
389 			}
390 
391 			/* Skip ports with an existing static mapping */
392 			if (bhndb_has_static_region_mapping(br, addr, size))
393 				continue;
394 
395 			/* Define a dynamic region for this port */
396 			error = bhndb_add_resource_region(br, addr, size,
397 			    pp->priority, pp->alloc_flags, NULL);
398 			if (error)
399 				return (error);
400 
401 			/* Update port mapping counts */
402 			switch (pp->priority) {
403 			case BHNDB_PRIORITY_NONE:
404 				break;
405 			case BHNDB_PRIORITY_LOW:
406 				prio_low++;
407 				break;
408 			case BHNDB_PRIORITY_DEFAULT:
409 				prio_default++;
410 				break;
411 			case BHNDB_PRIORITY_HIGH:
412 				prio_high++;
413 				break;
414 			}
415 		}
416 	}
417 
418 	/* Determine the minimum priority at which we'll allocate direct
419 	 * register windows from our dynamic pool */
420 	size_t prio_total = prio_low + prio_default + prio_high;
421 	if (prio_total <= br->dwa_count) {
422 		/* low+default+high priority regions get windows */
423 		br->min_prio = BHNDB_PRIORITY_LOW;
424 
425 	} else if (prio_default + prio_high <= br->dwa_count) {
426 		/* default+high priority regions get windows */
427 		br->min_prio = BHNDB_PRIORITY_DEFAULT;
428 
429 	} else {
430 		/* high priority regions get windows */
431 		br->min_prio = BHNDB_PRIORITY_HIGH;
432 	}
433 
434 	if (BHNDB_DEBUG(PRIO)) {
435 		struct bhndb_region	*region;
436 		const char		*direct_msg, *type_msg;
437 		bhndb_priority_t	 prio, prio_min;
438 		uint32_t		 flags;
439 
440 		prio_min = br->min_prio;
441 		device_printf(sc->dev, "min_prio: %d\n", prio_min);
442 
443 		STAILQ_FOREACH(region, &br->bus_regions, link) {
444 			prio = region->priority;
445 			flags = region->alloc_flags;
446 
447 			direct_msg = prio >= prio_min ? "direct" : "indirect";
448 			type_msg = region->static_regwin ? "static" : "dynamic";
449 
450 			device_printf(sc->dev, "region 0x%llx+0x%llx priority "
451 			    "%u %s/%s",
452 			    (unsigned long long) region->addr,
453 			    (unsigned long long) region->size,
454 			    region->priority,
455 			    direct_msg, type_msg);
456 
457 			if (flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
458 				printf(" [overcommit]\n");
459 			else
460 				printf("\n");
461 		}
462 	}
463 
464 	return (0);
465 }
466 
467 /**
468  * Find a hardware specification for @p dev.
469  *
470  * @param sc The bhndb device state.
471  * @param cores All cores enumerated on the bridged bhnd bus.
472  * @param ncores The length of @p cores.
473  * @param[out] hw On success, the matched hardware specification.
474  * with @p dev.
475  *
476  * @retval 0 success
477  * @retval non-zero if an error occurs fetching device info for comparison.
478  */
479 static int
bhndb_find_hwspec(struct bhndb_softc * sc,struct bhnd_core_info * cores,u_int ncores,const struct bhndb_hw ** hw)480 bhndb_find_hwspec(struct bhndb_softc *sc, struct bhnd_core_info *cores,
481     u_int ncores, const struct bhndb_hw **hw)
482 {
483 	const struct bhndb_hw	*next, *hw_table;
484 
485 	/* Search for the first matching hardware config. */
486 	hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev);
487 	for (next = hw_table; next->hw_reqs != NULL; next++) {
488 		if (!bhndb_hw_matches(sc, cores, ncores, next))
489 			continue;
490 
491 		/* Found */
492 		*hw = next;
493 		return (0);
494 	}
495 
496 	return (ENOENT);
497 }
498 
499 /**
500  * Helper function that must be called by subclass bhndb(4) drivers
501  * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4)
502  * APIs on the bridge device.
503  *
504  * This function will add a bridged bhnd(4) child device with a device order of
505  * BHND_PROBE_BUS. Any subclass bhndb(4) driver may use the BHND_PROBE_*
506  * priority bands to add additional devices that will be attached in
507  * their preferred order relative to the bridged bhnd(4) bus.
508  *
509  * @param dev		The bridge device to attach.
510  * @param cid		The bridged device's chip identification.
511  * @param cores		The bridged device's core table.
512  * @param ncores	The number of cores in @p cores.
513  * @param bridge_core	Core info for the bhnd(4) core serving as the host
514  *			bridge.
515  * @param erom_class	An erom parser class that may be used to parse
516  *			the bridged device's device enumeration table.
517  */
518 int
bhndb_attach(device_t dev,struct bhnd_chipid * cid,struct bhnd_core_info * cores,u_int ncores,struct bhnd_core_info * bridge_core,bhnd_erom_class_t * erom_class)519 bhndb_attach(device_t dev, struct bhnd_chipid *cid,
520     struct bhnd_core_info *cores, u_int ncores,
521     struct bhnd_core_info *bridge_core, bhnd_erom_class_t *erom_class)
522 {
523 	struct bhndb_devinfo		*dinfo;
524 	struct bhndb_softc		*sc;
525 	const struct bhndb_hw		*hw;
526 	const struct bhndb_hwcfg	*hwcfg;
527 	const struct bhndb_hw_priority	*hwprio;
528 	struct bhnd_erom_io		*eio;
529 	bhnd_erom_t			*erom;
530 	int				 error;
531 
532 	sc = device_get_softc(dev);
533 	sc->dev = dev;
534 	sc->parent_dev = device_get_parent(dev);
535 	sc->bridge_core = *bridge_core;
536 	sc->chipid = *cid;
537 
538 	if ((error = bhnd_service_registry_init(&sc->services)))
539 		return (error);
540 
541 	BHNDB_LOCK_INIT(sc);
542 
543 	erom = NULL;
544 
545 	/* Find a matching bridge hardware configuration */
546 	if ((error = bhndb_find_hwspec(sc, cores, ncores, &hw))) {
547 		device_printf(sc->dev, "unable to identify device, "
548 		    " using generic bridge resource definitions\n");
549 
550 		hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, dev);
551 		hw = NULL;
552 	} else {
553 		hwcfg = hw->cfg;
554 	}
555 
556 	if (hw != NULL && (bootverbose || BHNDB_DEBUG(PRIO))) {
557 		device_printf(sc->dev, "%s resource configuration\n", hw->name);
558 	}
559 
560 	/* Allocate bridge resource state using the discovered hardware
561 	 * configuration */
562 	sc->bus_res = bhndb_alloc_resources(sc->dev, sc->parent_dev, hwcfg);
563 	if (sc->bus_res == NULL) {
564 		device_printf(sc->dev, "failed to allocate bridge resource "
565 		    "state\n");
566 		error = ENOMEM;
567 		goto failed;
568 	}
569 
570 	/* Add our bridged bus device */
571 	sc->bus_dev = BUS_ADD_CHILD(dev, BHND_PROBE_BUS, "bhnd", -1);
572 	if (sc->bus_dev == NULL) {
573 		error = ENXIO;
574 		goto failed;
575 	}
576 
577 	dinfo = device_get_ivars(sc->bus_dev);
578 	dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
579 
580 	/* We can now use bhndb to perform bridging of SYS_RES_MEMORY resources;
581 	 * we use this to instantiate an erom parser instance */
582 	eio = bhnd_erom_iores_new(sc->bus_dev, 0);
583 	if ((erom = bhnd_erom_alloc(erom_class, cid, eio)) == NULL) {
584 		bhnd_erom_io_fini(eio);
585 		error = ENXIO;
586 		goto failed;
587 	}
588 
589 	/* Populate our resource priority configuration */
590 	hwprio = BHNDB_BUS_GET_HARDWARE_PRIO(sc->parent_dev, sc->dev);
591 	error = bhndb_init_region_cfg(sc, erom, sc->bus_res, cores, ncores,
592 	    hwprio);
593 	if (error) {
594 		device_printf(sc->dev, "failed to initialize resource "
595 		    "priority configuration: %d\n", error);
596 		goto failed;
597 	}
598 
599 	/* Free our erom instance */
600 	bhnd_erom_free(erom);
601 	erom = NULL;
602 
603 	return (0);
604 
605 failed:
606 	BHNDB_LOCK_DESTROY(sc);
607 
608 	if (sc->bus_res != NULL)
609 		bhndb_free_resources(sc->bus_res);
610 
611 	if (erom != NULL)
612 		bhnd_erom_free(erom);
613 
614 	bhnd_service_registry_fini(&sc->services);
615 
616 	return (error);
617 }
618 
619 /**
620  * Default bhndb(4) implementation of DEVICE_DETACH().
621  *
622  * This function detaches any child devices, and if successful, releases all
623  * resources held by the bridge device.
624  */
625 int
bhndb_generic_detach(device_t dev)626 bhndb_generic_detach(device_t dev)
627 {
628 	struct bhndb_softc	*sc;
629 	int			 error;
630 
631 	sc = device_get_softc(dev);
632 
633 	/* Detach children */
634 	if ((error = bus_generic_detach(dev)))
635 		return (error);
636 
637 	/* Delete children */
638 	if ((error = device_delete_children(dev)))
639 		return (error);
640 
641 	/* Clean up our service registry */
642 	if ((error = bhnd_service_registry_fini(&sc->services)))
643 		return (error);
644 
645 	/* Clean up our driver state. */
646 	bhndb_free_resources(sc->bus_res);
647 
648 	BHNDB_LOCK_DESTROY(sc);
649 
650 	return (0);
651 }
652 
653 /**
654  * Default bhndb(4) implementation of DEVICE_SUSPEND().
655  *
656  * This function calls bus_generic_suspend() (or implements equivalent
657  * behavior).
658  */
659 int
bhndb_generic_suspend(device_t dev)660 bhndb_generic_suspend(device_t dev)
661 {
662 	return (bus_generic_suspend(dev));
663 }
664 
665 /**
666  * Default bhndb(4) implementation of DEVICE_RESUME().
667  *
668  * This function calls bus_generic_resume() (or implements equivalent
669  * behavior).
670  */
671 int
bhndb_generic_resume(device_t dev)672 bhndb_generic_resume(device_t dev)
673 {
674 	struct bhndb_softc	*sc;
675 	struct bhndb_resources	*bus_res;
676 	struct bhndb_dw_alloc	*dwa;
677 	int			 error;
678 
679 	sc = device_get_softc(dev);
680 	bus_res = sc->bus_res;
681 
682 	/* Guarantee that all in-use dynamic register windows are mapped to
683 	 * their previously configured target address. */
684 	BHNDB_LOCK(sc);
685 	error = 0;
686 	for (size_t i = 0; i < bus_res->dwa_count; i++) {
687 		dwa = &bus_res->dw_alloc[i];
688 
689 		/* Skip regions that were not previously used */
690 		if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0)
691 			continue;
692 
693 		/* Otherwise, ensure the register window is correct before
694 		 * any children attempt MMIO */
695 		error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target);
696 		if (error)
697 			break;
698 	}
699 	BHNDB_UNLOCK(sc);
700 
701 	/* Error restoring hardware state; children cannot be safely resumed */
702 	if (error) {
703 		device_printf(dev, "Unable to restore hardware configuration; "
704 		    "cannot resume: %d\n", error);
705 		return (error);
706 	}
707 
708 	return (bus_generic_resume(dev));
709 }
710 
711 /**
712  * Default implementation of BHNDB_SUSPEND_RESOURCE.
713  */
714 static void
bhndb_suspend_resource(device_t dev,device_t child,int type,struct resource * r)715 bhndb_suspend_resource(device_t dev, device_t child, int type,
716     struct resource *r)
717 {
718 	struct bhndb_softc	*sc;
719 	struct bhndb_dw_alloc	*dwa;
720 
721 	sc = device_get_softc(dev);
722 
723 	/* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */
724 	if (type != SYS_RES_MEMORY)
725 		return;
726 
727 	BHNDB_LOCK(sc);
728 	dwa = bhndb_dw_find_resource(sc->bus_res, r);
729 	if (dwa == NULL) {
730 		BHNDB_UNLOCK(sc);
731 		return;
732 	}
733 
734 	if (BHNDB_DEBUG(PRIO))
735 		device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n",
736 		    type, rman_get_start(r), rman_get_size(r));
737 
738 	/* Release the resource's window reference */
739 	bhndb_dw_release(sc->bus_res, dwa, r);
740 	BHNDB_UNLOCK(sc);
741 }
742 
743 /**
744  * Default implementation of BHNDB_RESUME_RESOURCE.
745  */
746 static int
bhndb_resume_resource(device_t dev,device_t child,int type,struct resource * r)747 bhndb_resume_resource(device_t dev, device_t child, int type,
748     struct resource *r)
749 {
750 	struct bhndb_softc	*sc;
751 
752 	sc = device_get_softc(dev);
753 
754 	/* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */
755 	if (type != SYS_RES_MEMORY)
756 		return (0);
757 
758 	/* Inactive resources don't require reallocation of bridge resources */
759 	if (!(rman_get_flags(r) & RF_ACTIVE))
760 		return (0);
761 
762 	if (BHNDB_DEBUG(PRIO))
763 		device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n",
764 		    type, rman_get_start(r), rman_get_size(r));
765 
766 	return (bhndb_try_activate_resource(sc, rman_get_device(r), type,
767 	    rman_get_rid(r), r, NULL));
768 }
769 
770 /**
771  * Default bhndb(4) implementation of BUS_READ_IVAR().
772  */
773 static int
bhndb_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)774 bhndb_read_ivar(device_t dev, device_t child, int index,
775     uintptr_t *result)
776 {
777 	return (ENOENT);
778 }
779 
780 /**
781  * Default bhndb(4) implementation of BUS_WRITE_IVAR().
782  */
783 static int
bhndb_write_ivar(device_t dev,device_t child,int index,uintptr_t value)784 bhndb_write_ivar(device_t dev, device_t child, int index,
785     uintptr_t value)
786 {
787 	return (ENOENT);
788 }
789 
790 /**
791  * Return the address space for the given @p child device.
792  */
793 bhndb_addrspace
bhndb_get_addrspace(struct bhndb_softc * sc,device_t child)794 bhndb_get_addrspace(struct bhndb_softc *sc, device_t child)
795 {
796 	struct bhndb_devinfo	*dinfo;
797 	device_t		 imd_dev;
798 
799 	/* Find the directly attached parent of the requesting device */
800 	imd_dev = child;
801 	while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev)
802 		imd_dev = device_get_parent(imd_dev);
803 
804 	if (imd_dev == NULL)
805 		panic("bhndb address space request for non-child device %s\n",
806 		     device_get_nameunit(child));
807 
808 	dinfo = device_get_ivars(imd_dev);
809 	return (dinfo->addrspace);
810 }
811 
812 /**
813  * Return the rman instance for a given resource @p type, if any.
814  *
815  * @param sc The bhndb device state.
816  * @param child The requesting child.
817  * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...)
818  */
819 static struct rman *
bhndb_get_rman(struct bhndb_softc * sc,device_t child,int type)820 bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
821 {
822 	switch (bhndb_get_addrspace(sc, child)) {
823 	case BHNDB_ADDRSPACE_NATIVE:
824 		switch (type) {
825 		case SYS_RES_MEMORY:
826 			return (&sc->bus_res->ht_mem_rman);
827 		case SYS_RES_IRQ:
828 			return (NULL);
829 		default:
830 			return (NULL);
831 		}
832 
833 	case BHNDB_ADDRSPACE_BRIDGED:
834 		switch (type) {
835 		case SYS_RES_MEMORY:
836 			return (&sc->bus_res->br_mem_rman);
837 		case SYS_RES_IRQ:
838 			return (&sc->bus_res->br_irq_rman);
839 		default:
840 			return (NULL);
841 		}
842 	}
843 
844 	/* Quieten gcc */
845 	return (NULL);
846 }
847 
848 /**
849  * Default implementation of BUS_ADD_CHILD()
850  */
851 static device_t
bhndb_add_child(device_t dev,u_int order,const char * name,int unit)852 bhndb_add_child(device_t dev, u_int order, const char *name, int unit)
853 {
854 	struct bhndb_devinfo	*dinfo;
855 	device_t		 child;
856 
857 	child = device_add_child_ordered(dev, order, name, unit);
858 	if (child == NULL)
859 		return (NULL);
860 
861 	dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT);
862 	if (dinfo == NULL) {
863 		device_delete_child(dev, child);
864 		return (NULL);
865 	}
866 
867 	dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
868 	resource_list_init(&dinfo->resources);
869 
870 	device_set_ivars(child, dinfo);
871 
872 	return (child);
873 }
874 
875 /**
876  * Default implementation of BUS_CHILD_DELETED().
877  */
878 static void
bhndb_child_deleted(device_t dev,device_t child)879 bhndb_child_deleted(device_t dev, device_t child)
880 {
881 	struct bhndb_devinfo *dinfo = device_get_ivars(child);
882 	if (dinfo != NULL) {
883 		resource_list_free(&dinfo->resources);
884 		free(dinfo, M_BHND);
885 	}
886 
887 	device_set_ivars(child, NULL);
888 }
889 
890 /**
891  * Default implementation of BHNDB_GET_CHIPID().
892  */
893 static const struct bhnd_chipid *
bhndb_get_chipid(device_t dev,device_t child)894 bhndb_get_chipid(device_t dev, device_t child)
895 {
896 	struct bhndb_softc *sc = device_get_softc(dev);
897 	return (&sc->chipid);
898 }
899 
900 /**
901  * Default implementation of BHNDB_IS_CORE_DISABLED().
902  */
903 static bool
bhndb_is_core_disabled(device_t dev,device_t child,struct bhnd_core_info * core)904 bhndb_is_core_disabled(device_t dev, device_t child,
905     struct bhnd_core_info *core)
906 {
907 	struct bhndb_softc	*sc;
908 
909 	sc = device_get_softc(dev);
910 
911 	/* Try to defer to the bhndb bus parent */
912 	if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, core))
913 		return (true);
914 
915 	/* Otherwise, we treat bridge-capable cores as unpopulated if they're
916 	 * not the configured host bridge */
917 	if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(core)))
918 		return (!bhnd_cores_equal(core, &sc->bridge_core));
919 
920 	/* Assume the core is populated */
921 	return (false);
922 }
923 
924 /**
925  * Default bhndb(4) implementation of BHNDB_GET_HOSTB_CORE().
926  *
927  * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged
928  * bhnd(4) devices.
929  */
930 static int
bhndb_get_hostb_core(device_t dev,device_t child,struct bhnd_core_info * core)931 bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core)
932 {
933 	struct bhndb_softc *sc = device_get_softc(dev);
934 
935 	*core = sc->bridge_core;
936 	return (0);
937 }
938 
939 /**
940  * Default bhndb(4) implementation of BHND_BUS_GET_SERVICE_REGISTRY().
941  */
942 static struct bhnd_service_registry *
bhndb_get_service_registry(device_t dev,device_t child)943 bhndb_get_service_registry(device_t dev, device_t child)
944 {
945 	struct bhndb_softc *sc = device_get_softc(dev);
946 
947 	return (&sc->services);
948 }
949 
950 /**
951  * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE().
952  */
953 static struct resource *
bhndb_alloc_resource(device_t dev,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)954 bhndb_alloc_resource(device_t dev, device_t child, int type,
955     int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
956 {
957 	struct bhndb_softc		*sc;
958 	struct resource_list_entry	*rle;
959 	struct resource			*rv;
960 	struct rman			*rm;
961 	int				 error;
962 	bool				 passthrough, isdefault;
963 
964 	sc = device_get_softc(dev);
965 	passthrough = (device_get_parent(child) != dev);
966 	isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
967 	rle = NULL;
968 
969 	/* Fetch the resource manager */
970 	rm = bhndb_get_rman(sc, child, type);
971 	if (rm == NULL) {
972 		/* Delegate to our parent device's bus; the requested
973 		 * resource type isn't handled locally. */
974 		return (BUS_ALLOC_RESOURCE(device_get_parent(sc->parent_dev),
975 		    child, type, rid,  start, end, count, flags));
976 	}
977 
978 	/* Populate defaults */
979 	if (!passthrough && isdefault) {
980 		/* Fetch the resource list entry. */
981 		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
982 		    type, *rid);
983 		if (rle == NULL) {
984 			device_printf(dev,
985 			    "default resource %#x type %d for child %s "
986 			    "not found\n", *rid, type,
987 			    device_get_nameunit(child));
988 
989 			return (NULL);
990 		}
991 
992 		if (rle->res != NULL) {
993 			device_printf(dev,
994 			    "resource entry %#x type %d for child %s is busy\n",
995 			    *rid, type, device_get_nameunit(child));
996 
997 			return (NULL);
998 		}
999 
1000 		start = rle->start;
1001 		end = rle->end;
1002 		count = ulmax(count, rle->count);
1003 	}
1004 
1005 	/* Validate resource addresses */
1006 	if (start > end || count > ((end - start) + 1))
1007 		return (NULL);
1008 
1009 	/* Make our reservation */
1010 	rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
1011 	    child);
1012 	if (rv == NULL)
1013 		return (NULL);
1014 
1015 	rman_set_rid(rv, *rid);
1016 
1017 	/* Activate */
1018 	if (flags & RF_ACTIVE) {
1019 		error = bus_activate_resource(child, type, *rid, rv);
1020 		if (error) {
1021 			device_printf(dev,
1022 			    "failed to activate entry %#x type %d for "
1023 				"child %s: %d\n",
1024 			     *rid, type, device_get_nameunit(child), error);
1025 
1026 			rman_release_resource(rv);
1027 
1028 			return (NULL);
1029 		}
1030 	}
1031 
1032 	/* Update child's resource list entry */
1033 	if (rle != NULL) {
1034 		rle->res = rv;
1035 		rle->start = rman_get_start(rv);
1036 		rle->end = rman_get_end(rv);
1037 		rle->count = rman_get_size(rv);
1038 	}
1039 
1040 	return (rv);
1041 }
1042 
1043 /**
1044  * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE().
1045  */
1046 static int
bhndb_release_resource(device_t dev,device_t child,int type,int rid,struct resource * r)1047 bhndb_release_resource(device_t dev, device_t child, int type, int rid,
1048     struct resource *r)
1049 {
1050 	struct bhndb_softc		*sc;
1051 	struct resource_list_entry	*rle;
1052 	bool				 passthrough;
1053 	int				 error;
1054 
1055 	sc = device_get_softc(dev);
1056 	passthrough = (device_get_parent(child) != dev);
1057 
1058 	/* Delegate to our parent device's bus if the requested resource type
1059 	 * isn't handled locally. */
1060 	if (bhndb_get_rman(sc, child, type) == NULL) {
1061 		return (BUS_RELEASE_RESOURCE(device_get_parent(sc->parent_dev),
1062 		    child, type, rid, r));
1063 	}
1064 
1065 	/* Deactivate resources */
1066 	if (rman_get_flags(r) & RF_ACTIVE) {
1067 		error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
1068 		if (error)
1069 			return (error);
1070 	}
1071 
1072 	if ((error = rman_release_resource(r)))
1073 		return (error);
1074 
1075 	if (!passthrough) {
1076 		/* Clean resource list entry */
1077 		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
1078 		    type, rid);
1079 		if (rle != NULL)
1080 			rle->res = NULL;
1081 	}
1082 
1083 	return (0);
1084 }
1085 
1086 /**
1087  * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE().
1088  */
1089 static int
bhndb_adjust_resource(device_t dev,device_t child,int type,struct resource * r,rman_res_t start,rman_res_t end)1090 bhndb_adjust_resource(device_t dev, device_t child, int type,
1091     struct resource *r, rman_res_t start, rman_res_t end)
1092 {
1093 	struct bhndb_softc		*sc;
1094 	struct rman			*rm;
1095 	rman_res_t			 mstart, mend;
1096 	int				 error;
1097 
1098 	sc = device_get_softc(dev);
1099 	error = 0;
1100 
1101 	/* Delegate to our parent device's bus if the requested resource type
1102 	 * isn't handled locally. */
1103 	rm = bhndb_get_rman(sc, child, type);
1104 	if (rm == NULL) {
1105 		return (BUS_ADJUST_RESOURCE(device_get_parent(sc->parent_dev),
1106 		    child, type, r, start, end));
1107 	}
1108 
1109 	/* Verify basic constraints */
1110 	if (end <= start)
1111 		return (EINVAL);
1112 
1113 	if (!rman_is_region_manager(r, rm))
1114 		return (ENXIO);
1115 
1116 	BHNDB_LOCK(sc);
1117 
1118 	/* If not active, allow any range permitted by the resource manager */
1119 	if (!(rman_get_flags(r) & RF_ACTIVE))
1120 		goto done;
1121 
1122 	/* Otherwise, the range is limited by the bridged resource mapping */
1123 	error = bhndb_find_resource_limits(sc->bus_res, type, r, &mstart,
1124 	    &mend);
1125 	if (error)
1126 		goto done;
1127 
1128 	if (start < mstart || end > mend) {
1129 		error = EINVAL;
1130 		goto done;
1131 	}
1132 
1133 	/* Fall through */
1134 done:
1135 	if (!error)
1136 		error = rman_adjust_resource(r, start, end);
1137 
1138 	BHNDB_UNLOCK(sc);
1139 	return (error);
1140 }
1141 
1142 /**
1143  * Initialize child resource @p r with a virtual address, tag, and handle
1144  * copied from @p parent, adjusted to contain only the range defined by
1145  * @p offsize and @p size.
1146  *
1147  * @param r The register to be initialized.
1148  * @param parent The parent bus resource that fully contains the subregion.
1149  * @param offset The subregion offset within @p parent.
1150  * @param size The subregion size.
1151  * @p r.
1152  */
1153 static int
bhndb_init_child_resource(struct resource * r,struct resource * parent,bhnd_size_t offset,bhnd_size_t size)1154 bhndb_init_child_resource(struct resource *r,
1155     struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
1156 {
1157 	bus_space_handle_t	bh, child_bh;
1158 	bus_space_tag_t		bt;
1159 	uintptr_t		vaddr;
1160 	int			error;
1161 
1162 	/* Fetch the parent resource's real bus values */
1163 	vaddr = (uintptr_t) rman_get_virtual(parent);
1164 	bt = rman_get_bustag(parent);
1165 	bh = rman_get_bushandle(parent);
1166 
1167 	/* Configure child resource with window-adjusted real bus values */
1168 	vaddr += offset;
1169 	error = bus_space_subregion(bt, bh, offset, size, &child_bh);
1170 	if (error)
1171 		return (error);
1172 
1173 	rman_set_virtual(r, (void *) vaddr);
1174 	rman_set_bustag(r, bt);
1175 	rman_set_bushandle(r, child_bh);
1176 
1177 	return (0);
1178 }
1179 
1180 /**
1181  * Attempt activation of a fixed register window mapping for @p child.
1182  *
1183  * @param sc BHNDB device state.
1184  * @param region The static region definition capable of mapping @p r.
1185  * @param child A child requesting resource activation.
1186  * @param type Resource type.
1187  * @param rid Resource identifier.
1188  * @param r Resource to be activated.
1189  *
1190  * @retval 0 if @p r was activated successfully
1191  * @retval ENOENT if no fixed register window was found.
1192  * @retval non-zero if @p r could not be activated.
1193  */
1194 static int
bhndb_activate_static_region(struct bhndb_softc * sc,struct bhndb_region * region,device_t child,int type,int rid,struct resource * r)1195 bhndb_activate_static_region(struct bhndb_softc *sc,
1196     struct bhndb_region *region, device_t child, int type, int rid,
1197     struct resource *r)
1198 {
1199 	struct resource			*bridge_res;
1200 	const struct bhndb_regwin	*win;
1201 	bhnd_size_t			 parent_offset;
1202 	rman_res_t			 r_start, r_size;
1203 	int				 error;
1204 
1205 	win = region->static_regwin;
1206 
1207 	KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type),
1208 	    ("can't activate non-static region"));
1209 
1210 	r_start = rman_get_start(r);
1211 	r_size = rman_get_size(r);
1212 
1213 	/* Find the corresponding bridge resource */
1214 	bridge_res = bhndb_host_resource_for_regwin(sc->bus_res->res, win);
1215 	if (bridge_res == NULL)
1216 		return (ENXIO);
1217 
1218 	/* Calculate subregion offset within the parent resource */
1219 	parent_offset = r_start - region->addr;
1220 	parent_offset += win->win_offset;
1221 
1222 	/* Configure resource with its real bus values. */
1223 	error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size);
1224 	if (error)
1225 		return (error);
1226 
1227 	/* Mark active */
1228 	if ((error = rman_activate_resource(r)))
1229 		return (error);
1230 
1231 	return (0);
1232 }
1233 
1234 /**
1235  * Attempt to allocate/retain a dynamic register window for @p r, returning
1236  * the retained window.
1237  *
1238  * @param sc The bhndb driver state.
1239  * @param r The resource for which a window will be retained.
1240  */
1241 static struct bhndb_dw_alloc *
bhndb_retain_dynamic_window(struct bhndb_softc * sc,struct resource * r)1242 bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r)
1243 {
1244 	struct bhndb_dw_alloc	*dwa;
1245 	rman_res_t		 r_start, r_size;
1246 	int			 error;
1247 
1248 	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1249 
1250 	r_start = rman_get_start(r);
1251 	r_size = rman_get_size(r);
1252 
1253 	/* Look for an existing dynamic window we can reference */
1254 	dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size);
1255 	if (dwa != NULL) {
1256 		if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0)
1257 			return (dwa);
1258 
1259 		return (NULL);
1260 	}
1261 
1262 	/* Otherwise, try to reserve a free window */
1263 	dwa = bhndb_dw_next_free(sc->bus_res);
1264 	if (dwa == NULL) {
1265 		/* No free windows */
1266 		return (NULL);
1267 	}
1268 
1269 	/* Window must be large enough to map the entire resource */
1270 	if (dwa->win->win_size < rman_get_size(r))
1271 		return (NULL);
1272 
1273 	/* Set the window target */
1274 	error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r),
1275 	    rman_get_size(r));
1276 	if (error) {
1277 		device_printf(sc->dev, "dynamic window initialization "
1278 			"for 0x%llx-0x%llx failed: %d\n",
1279 			(unsigned long long) r_start,
1280 			(unsigned long long) r_start + r_size - 1,
1281 			error);
1282 		return (NULL);
1283 	}
1284 
1285 	/* Add our reservation */
1286 	if (bhndb_dw_retain(sc->bus_res, dwa, r))
1287 		return (NULL);
1288 
1289 	return (dwa);
1290 }
1291 
1292 /**
1293  * Activate a resource using any viable static or dynamic register window.
1294  *
1295  * @param sc The bhndb driver state.
1296  * @param child The child holding ownership of @p r.
1297  * @param type The type of the resource to be activated.
1298  * @param rid The resource ID of @p r.
1299  * @param r The resource to be activated
1300  * @param[out] indirect On error and if not NULL, will be set to 'true' if
1301  * the caller should instead use an indirect resource mapping.
1302  *
1303  * @retval 0 success
1304  * @retval non-zero activation failed.
1305  */
1306 static int
bhndb_try_activate_resource(struct bhndb_softc * sc,device_t child,int type,int rid,struct resource * r,bool * indirect)1307 bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
1308     int rid, struct resource *r, bool *indirect)
1309 {
1310 	struct bhndb_region	*region;
1311 	struct bhndb_dw_alloc	*dwa;
1312 	bhndb_priority_t	 dw_priority;
1313 	rman_res_t		 r_start, r_size;
1314 	rman_res_t		 parent_offset;
1315 	int			 error;
1316 
1317 	BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
1318 
1319 	if (indirect != NULL)
1320 		*indirect = false;
1321 
1322 	switch (type) {
1323 	case SYS_RES_IRQ:
1324 		/* IRQ resources are always directly mapped */
1325 		return (rman_activate_resource(r));
1326 
1327 	case SYS_RES_MEMORY:
1328 		/* Handled below */
1329 		break;
1330 
1331 	default:
1332 		device_printf(sc->dev, "unsupported resource type %d\n", type);
1333 		return (ENXIO);
1334 	}
1335 
1336 	/* Only MMIO resources can be mapped via register windows */
1337 	KASSERT(type == SYS_RES_MEMORY, ("invalid type: %d", type));
1338 
1339 	r_start = rman_get_start(r);
1340 	r_size = rman_get_size(r);
1341 
1342 	/* Activate native addrspace resources using the host address space */
1343 	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) {
1344 		struct resource *parent;
1345 
1346 		/* Find the bridge resource referenced by the child */
1347 		parent = bhndb_host_resource_for_range(sc->bus_res->res,
1348 		    type, r_start, r_size);
1349 		if (parent == NULL) {
1350 			device_printf(sc->dev, "host resource not found "
1351 			     "for 0x%llx-0x%llx\n",
1352 			     (unsigned long long) r_start,
1353 			     (unsigned long long) r_start + r_size - 1);
1354 			return (ENOENT);
1355 		}
1356 
1357 		/* Initialize child resource with the real bus values */
1358 		error = bhndb_init_child_resource(r, parent,
1359 		    r_start - rman_get_start(parent), r_size);
1360 		if (error)
1361 			return (error);
1362 
1363 		/* Try to activate child resource */
1364 		return (rman_activate_resource(r));
1365 	}
1366 
1367 	/* Default to low priority */
1368 	dw_priority = BHNDB_PRIORITY_LOW;
1369 
1370 	/* Look for a bus region matching the resource's address range */
1371 	region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
1372 	if (region != NULL)
1373 		dw_priority = region->priority;
1374 
1375 	/* Prefer static mappings over consuming a dynamic windows. */
1376 	if (region && region->static_regwin) {
1377 		error = bhndb_activate_static_region(sc, region, child, type,
1378 		    rid, r);
1379 		if (error)
1380 			device_printf(sc->dev, "static window allocation "
1381 			     "for 0x%llx-0x%llx failed\n",
1382 			     (unsigned long long) r_start,
1383 			     (unsigned long long) r_start + r_size - 1);
1384 		return (error);
1385 	}
1386 
1387 	/* A dynamic window will be required; is this resource high enough
1388 	 * priority to be reserved a dynamic window? */
1389 	if (dw_priority < sc->bus_res->min_prio) {
1390 		if (indirect)
1391 			*indirect = true;
1392 
1393 		return (ENOMEM);
1394 	}
1395 
1396 	/* Find and retain a usable window */
1397 	BHNDB_LOCK(sc); {
1398 		dwa = bhndb_retain_dynamic_window(sc, r);
1399 	} BHNDB_UNLOCK(sc);
1400 
1401 	if (dwa == NULL) {
1402 		if (indirect)
1403 			*indirect = true;
1404 		return (ENOMEM);
1405 	}
1406 
1407 	/* Configure resource with its real bus values. */
1408 	parent_offset = dwa->win->win_offset;
1409 	parent_offset += r_start - dwa->target;
1410 
1411 	error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset,
1412 	    dwa->win->win_size);
1413 	if (error)
1414 		goto failed;
1415 
1416 	/* Mark active */
1417 	if ((error = rman_activate_resource(r)))
1418 		goto failed;
1419 
1420 	return (0);
1421 
1422 failed:
1423 	/* Release our region allocation. */
1424 	BHNDB_LOCK(sc);
1425 	bhndb_dw_release(sc->bus_res, dwa, r);
1426 	BHNDB_UNLOCK(sc);
1427 
1428 	return (error);
1429 }
1430 
1431 /**
1432  * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE().
1433  */
1434 static int
bhndb_activate_resource(device_t dev,device_t child,int type,int rid,struct resource * r)1435 bhndb_activate_resource(device_t dev, device_t child, int type, int rid,
1436     struct resource *r)
1437 {
1438 	struct bhndb_softc *sc = device_get_softc(dev);
1439 
1440 	/* Delegate directly to our parent device's bus if the requested
1441 	 * resource type isn't handled locally. */
1442 	if (bhndb_get_rman(sc, child, type) == NULL) {
1443 		return (BUS_ACTIVATE_RESOURCE(device_get_parent(sc->parent_dev),
1444 		    child, type, rid, r));
1445 	}
1446 
1447 	return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL));
1448 }
1449 
1450 /**
1451  * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE().
1452  */
1453 static int
bhndb_deactivate_resource(device_t dev,device_t child,int type,int rid,struct resource * r)1454 bhndb_deactivate_resource(device_t dev, device_t child, int type,
1455     int rid, struct resource *r)
1456 {
1457 	struct bhndb_dw_alloc	*dwa;
1458 	struct bhndb_softc	*sc;
1459 	struct rman		*rm;
1460 	int			 error;
1461 
1462 	sc = device_get_softc(dev);
1463 
1464 	/* Delegate directly to our parent device's bus if the requested
1465 	 * resource type isn't handled locally. */
1466 	rm = bhndb_get_rman(sc, child, type);
1467 	if (rm == NULL) {
1468 		return (BUS_DEACTIVATE_RESOURCE(
1469 		    device_get_parent(sc->parent_dev), child, type, rid, r));
1470 	}
1471 
1472 	/* Mark inactive */
1473 	if ((error = rman_deactivate_resource(r)))
1474 		return (error);
1475 
1476 	switch (type) {
1477 	case SYS_RES_IRQ:
1478 		/* No bridge-level state to be freed */
1479 		return (0);
1480 
1481 	case SYS_RES_MEMORY:
1482 		/* Free any dynamic window allocation. */
1483 		if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1484 			BHNDB_LOCK(sc);
1485 			dwa = bhndb_dw_find_resource(sc->bus_res, r);
1486 			if (dwa != NULL)
1487 				bhndb_dw_release(sc->bus_res, dwa, r);
1488 			BHNDB_UNLOCK(sc);
1489 		}
1490 
1491 		return (0);
1492 
1493 	default:
1494 		device_printf(dev, "unsupported resource type %d\n", type);
1495 		return (ENXIO);
1496 	}
1497 }
1498 
1499 /**
1500  * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST().
1501  */
1502 static struct resource_list *
bhndb_get_resource_list(device_t dev,device_t child)1503 bhndb_get_resource_list(device_t dev, device_t child)
1504 {
1505 	struct bhndb_devinfo *dinfo = device_get_ivars(child);
1506 	return (&dinfo->resources);
1507 }
1508 
1509 /**
1510  * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE().
1511  *
1512  * For BHNDB_ADDRSPACE_NATIVE children, all resources are activated as direct
1513  * resources via BUS_ACTIVATE_RESOURCE().
1514  *
1515  * For BHNDB_ADDRSPACE_BRIDGED children, the resource priority is determined,
1516  * and if possible, the resource is activated as a direct resource. For example,
1517  * depending on resource priority and bridge resource availability, this
1518  * function will attempt to activate SYS_RES_MEMORY resources using either a
1519  * static register window, a dynamic register window, or it will configure @p r
1520  * as an indirect resource -- in that order.
1521  */
1522 static int
bhndb_activate_bhnd_resource(device_t dev,device_t child,int type,int rid,struct bhnd_resource * r)1523 bhndb_activate_bhnd_resource(device_t dev, device_t child,
1524     int type, int rid, struct bhnd_resource *r)
1525 {
1526 	struct bhndb_softc	*sc;
1527 	struct bhndb_region	*region;
1528 	bhndb_priority_t	 r_prio;
1529 	rman_res_t		 r_start, r_size;
1530 	int 			 error;
1531 	bool			 indirect;
1532 
1533 	KASSERT(!r->direct,
1534 	    ("direct flag set on inactive resource"));
1535 
1536 	KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE),
1537 	    ("RF_ACTIVE set on inactive resource"));
1538 
1539 	sc = device_get_softc(dev);
1540 
1541 	/* Delegate directly to BUS_ACTIVATE_RESOURCE() if the requested
1542 	 * resource type isn't handled locally. */
1543 	if (bhndb_get_rman(sc, child, type) == NULL) {
1544 		error = BUS_ACTIVATE_RESOURCE(dev, child, type, rid, r->res);
1545 		if (error == 0)
1546 			r->direct = true;
1547 		return (error);
1548 	}
1549 
1550 	r_start = rman_get_start(r->res);
1551 	r_size = rman_get_size(r->res);
1552 
1553 	/* Determine the resource priority of bridged resources, and skip direct
1554 	 * allocation if the priority is too low. */
1555 	if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
1556 		switch (type) {
1557 		case SYS_RES_IRQ:
1558 			/* IRQ resources are always direct */
1559 			break;
1560 
1561 		case SYS_RES_MEMORY:
1562 			region = bhndb_find_resource_region(sc->bus_res,
1563 				r_start, r_size);
1564 			if (region != NULL)
1565 				r_prio = region->priority;
1566 			else
1567 				r_prio = BHNDB_PRIORITY_NONE;
1568 
1569 			/* If less than the minimum dynamic window priority,
1570 			 * this resource should always be indirect. */
1571 			if (r_prio < sc->bus_res->min_prio)
1572 				return (0);
1573 
1574 			break;
1575 
1576 		default:
1577 			device_printf(dev, "unsupported resource type %d\n",
1578 			    type);
1579 			return (ENXIO);
1580 		}
1581 	}
1582 
1583 	/* Attempt direct activation */
1584 	error = bhndb_try_activate_resource(sc, child, type, rid, r->res,
1585 	    &indirect);
1586 	if (!error) {
1587 		r->direct = true;
1588 	} else if (indirect) {
1589 		/* The request was valid, but no viable register window is
1590 		 * available; indirection must be employed. */
1591 		error = 0;
1592 		r->direct = false;
1593 	}
1594 
1595 	if (BHNDB_DEBUG(PRIO) &&
1596 	    bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED)
1597 	{
1598 		device_printf(child, "activated 0x%llx-0x%llx as %s "
1599 		    "resource\n",
1600 		    (unsigned long long) r_start,
1601 		    (unsigned long long) r_start + r_size - 1,
1602 		    r->direct ? "direct" : "indirect");
1603 	}
1604 
1605 	return (error);
1606 }
1607 
1608 /**
1609  * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE().
1610  */
1611 static int
bhndb_deactivate_bhnd_resource(device_t dev,device_t child,int type,int rid,struct bhnd_resource * r)1612 bhndb_deactivate_bhnd_resource(device_t dev, device_t child,
1613     int type, int rid, struct bhnd_resource *r)
1614 {
1615 	int error;
1616 
1617 	/* Indirect resources don't require activation */
1618 	if (!r->direct)
1619 		return (0);
1620 
1621 	KASSERT(rman_get_flags(r->res) & RF_ACTIVE,
1622 	    ("RF_ACTIVE not set on direct resource"));
1623 
1624 	/* Perform deactivation */
1625 	error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r->res);
1626 	if (!error)
1627 		r->direct = false;
1628 
1629 	return (error);
1630 }
1631 
1632 /**
1633  * Find the best available bridge resource allocation record capable of handling
1634  * bus I/O requests of @p size at @p addr.
1635  *
1636  * In order of preference, this function will either:
1637  *
1638  * - Configure and return a free allocation record
1639  * - Return an existing allocation record mapping the requested space, or
1640  * - Steal, configure, and return an in-use allocation record.
1641  *
1642  * Will panic if a usable record cannot be found.
1643  *
1644  * @param sc Bridge driver state.
1645  * @param addr The I/O target address.
1646  * @param size The size of the I/O operation to be performed at @p addr.
1647  * @param[out] borrowed Set to true if the allocation record was borrowed to
1648  * fulfill this request; the borrowed record maps the target address range,
1649  * and must not be modified.
1650  * @param[out] stolen Set to true if the allocation record was stolen to fulfill
1651  * this request. If a stolen allocation record is returned,
1652  * bhndb_io_resource_restore() must be called upon completion of the bus I/O
1653  * request.
1654  * @param[out] restore If the allocation record was stolen, this will be set
1655  * to the target that must be restored.
1656  */
1657 static struct bhndb_dw_alloc *
bhndb_io_resource_get_window(struct bhndb_softc * sc,bus_addr_t addr,bus_size_t size,bool * borrowed,bool * stolen,bus_addr_t * restore)1658 bhndb_io_resource_get_window(struct bhndb_softc *sc, bus_addr_t addr,
1659     bus_size_t size, bool *borrowed, bool *stolen, bus_addr_t *restore)
1660 {
1661 	struct bhndb_resources	*br;
1662 	struct bhndb_dw_alloc	*dwa;
1663 	struct bhndb_region	*region;
1664 
1665 	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1666 
1667 	br = sc->bus_res;
1668 	*borrowed = false;
1669 	*stolen = false;
1670 
1671 	/* Try to fetch a free window */
1672 	if ((dwa = bhndb_dw_next_free(br)) != NULL)
1673 		return (dwa);
1674 
1675 	/* Search for an existing dynamic mapping of this address range.
1676 	 * Static regions are not searched, as a statically mapped
1677 	 * region would never be allocated as an indirect resource. */
1678 	for (size_t i = 0; i < br->dwa_count; i++) {
1679 		const struct bhndb_regwin *win;
1680 
1681 		dwa = &br->dw_alloc[i];
1682 		win = dwa->win;
1683 
1684 		KASSERT(win->win_type == BHNDB_REGWIN_T_DYN,
1685 			("invalid register window type"));
1686 
1687 		/* Verify the range */
1688 		if (addr < dwa->target)
1689 			continue;
1690 
1691 		if (addr + size > dwa->target + win->win_size)
1692 			continue;
1693 
1694 		/* Found */
1695 		*borrowed = true;
1696 		return (dwa);
1697 	}
1698 
1699 	/* Try to steal a window; this should only be required on very early
1700 	 * PCI_V0 (BCM4318, etc) Wi-Fi chipsets */
1701 	region = bhndb_find_resource_region(br, addr, size);
1702 	if (region == NULL)
1703 		return (NULL);
1704 
1705 	if ((region->alloc_flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) == 0)
1706 		return (NULL);
1707 
1708 	/* Steal a window. This acquires our backing spinlock, disabling
1709 	 * interrupts; the spinlock will be released by
1710 	 * bhndb_dw_return_stolen() */
1711 	if ((dwa = bhndb_dw_steal(br, restore)) != NULL) {
1712 		*stolen = true;
1713 		return (dwa);
1714 	}
1715 
1716 	panic("register windows exhausted attempting to map 0x%llx-0x%llx\n",
1717 	    (unsigned long long) addr, (unsigned long long) addr+size-1);
1718 }
1719 
1720 /**
1721  * Return a borrowed reference to a bridge resource allocation record capable
1722  * of handling bus I/O requests of @p size at @p addr.
1723  *
1724  * This will either return a reference to an existing allocation record mapping
1725  * the requested space, or will configure and return a free allocation record.
1726  *
1727  * Will panic if a usable record cannot be found.
1728  *
1729  * @param sc Bridge driver state.
1730  * @param addr The I/O target address.
1731  * @param size The size of the I/O operation to be performed at @p addr.
1732  * @param[out] offset The offset within the returned resource at which
1733  * to perform the I/O request.
1734  * @param[out] stolen Set to true if the allocation record was stolen to fulfill
1735  * this request. If a stolen allocation record is returned,
1736  * bhndb_io_resource_restore() must be called upon completion of the bus I/O
1737  * request.
1738  * @param[out] restore If the allocation record was stolen, this will be set
1739  * to the target that must be restored.
1740  */
1741 static inline struct bhndb_dw_alloc *
bhndb_io_resource(struct bhndb_softc * sc,bus_addr_t addr,bus_size_t size,bus_size_t * offset,bool * stolen,bus_addr_t * restore)1742 bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
1743     bus_size_t *offset, bool *stolen, bus_addr_t *restore)
1744 {
1745 	struct bhndb_dw_alloc	*dwa;
1746 	bool			 borrowed;
1747 	int			 error;
1748 
1749 	BHNDB_LOCK_ASSERT(sc, MA_OWNED);
1750 
1751 	dwa = bhndb_io_resource_get_window(sc, addr, size, &borrowed, stolen,
1752 	    restore);
1753 
1754 	/* Adjust the window if the I/O request won't fit in the current
1755 	 * target range. */
1756 	if (addr < dwa->target ||
1757 	    addr > dwa->target + dwa->win->win_size ||
1758 	    (dwa->target + dwa->win->win_size) - addr < size)
1759 	{
1760 		/* Cannot modify target of borrowed windows */
1761 		if (borrowed) {
1762 			panic("borrowed register window does not map expected "
1763 			    "range 0x%llx-0x%llx\n",
1764 			    (unsigned long long) addr,
1765 			    (unsigned long long) addr+size-1);
1766 		}
1767 
1768 		error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr,
1769 		    size);
1770 		if (error) {
1771 		    panic("failed to set register window target mapping "
1772 			    "0x%llx-0x%llx\n",
1773 			    (unsigned long long) addr,
1774 			    (unsigned long long) addr+size-1);
1775 		}
1776 	}
1777 
1778 	/* Calculate the offset and return */
1779 	*offset = (addr - dwa->target) + dwa->win->win_offset;
1780 	return (dwa);
1781 }
1782 
1783 /*
1784  * BHND_BUS_(READ|WRITE_* implementations
1785  */
1786 
1787 /* bhndb_bus_(read|write) common implementation */
1788 #define	BHNDB_IO_COMMON_SETUP(_io_size)				\
1789 	struct bhndb_softc	*sc;				\
1790 	struct bhndb_dw_alloc	*dwa;				\
1791 	struct resource		*io_res;			\
1792 	bus_size_t		 io_offset;			\
1793 	bus_addr_t		 restore;		\
1794 	bool			 stolen;			\
1795 								\
1796 	sc = device_get_softc(dev);				\
1797 								\
1798 	BHNDB_LOCK(sc);						\
1799 	dwa = bhndb_io_resource(sc, rman_get_start(r->res) +	\
1800 	    offset, _io_size, &io_offset, &stolen, &restore);	\
1801 	io_res = dwa->parent_res;				\
1802 								\
1803 	KASSERT(!r->direct,					\
1804 	    ("bhnd_bus slow path used for direct resource"));	\
1805 								\
1806 	KASSERT(rman_get_flags(io_res) & RF_ACTIVE,		\
1807 	    ("i/o resource is not active"));
1808 
1809 #define	BHNDB_IO_COMMON_TEARDOWN()				\
1810 	if (stolen) {						\
1811 		bhndb_dw_return_stolen(sc->dev, sc->bus_res,	\
1812 		    dwa, restore);				\
1813 	}							\
1814 	BHNDB_UNLOCK(sc);
1815 
1816 /* Defines a bhndb_bus_read_* method implementation */
1817 #define	BHNDB_IO_READ(_type, _name)				\
1818 static _type							\
1819 bhndb_bus_read_ ## _name (device_t dev, device_t child,		\
1820     struct bhnd_resource *r, bus_size_t offset)			\
1821 {								\
1822 	_type v;						\
1823 	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1824 	v = bus_read_ ## _name (io_res, io_offset);		\
1825 	BHNDB_IO_COMMON_TEARDOWN();				\
1826 								\
1827 	return (v);						\
1828 }
1829 
1830 /* Defines a bhndb_bus_write_* method implementation */
1831 #define	BHNDB_IO_WRITE(_type, _name)				\
1832 static void							\
1833 bhndb_bus_write_ ## _name (device_t dev, device_t child,	\
1834     struct bhnd_resource *r, bus_size_t offset, _type value)	\
1835 {								\
1836 	BHNDB_IO_COMMON_SETUP(sizeof(_type));			\
1837 	bus_write_ ## _name (io_res, io_offset, value);		\
1838 	BHNDB_IO_COMMON_TEARDOWN();				\
1839 }
1840 
1841 /* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */
1842 #define	BHNDB_IO_MISC(_type, _ptr, _op, _size)			\
1843 static void							\
1844 bhndb_bus_ ## _op ## _ ## _size (device_t dev,			\
1845     device_t child, struct bhnd_resource *r, bus_size_t offset,	\
1846     _type _ptr datap, bus_size_t count)				\
1847 {								\
1848 	BHNDB_IO_COMMON_SETUP(sizeof(_type) * count);		\
1849 	bus_ ## _op ## _ ## _size (io_res, io_offset,		\
1850 	    datap, count);					\
1851 	BHNDB_IO_COMMON_TEARDOWN();				\
1852 }
1853 
1854 /* Defines a complete set of read/write methods */
1855 #define	BHNDB_IO_METHODS(_type, _size)				\
1856 	BHNDB_IO_READ(_type, _size)				\
1857 	BHNDB_IO_WRITE(_type, _size)				\
1858 								\
1859 	BHNDB_IO_READ(_type, stream_ ## _size)			\
1860 	BHNDB_IO_WRITE(_type, stream_ ## _size)			\
1861 								\
1862 	BHNDB_IO_MISC(_type, *, read_multi, _size)		\
1863 	BHNDB_IO_MISC(_type, *, write_multi, _size)		\
1864 								\
1865 	BHNDB_IO_MISC(_type, *, read_multi_stream, _size)	\
1866 	BHNDB_IO_MISC(_type, *, write_multi_stream, _size)	\
1867 								\
1868 	BHNDB_IO_MISC(_type,  , set_multi, _size)		\
1869 	BHNDB_IO_MISC(_type,  , set_region, _size)		\
1870 	BHNDB_IO_MISC(_type, *, read_region, _size)		\
1871 	BHNDB_IO_MISC(_type, *, write_region, _size)		\
1872 								\
1873 	BHNDB_IO_MISC(_type, *, read_region_stream, _size)	\
1874 	BHNDB_IO_MISC(_type, *, write_region_stream, _size)
1875 
1876 BHNDB_IO_METHODS(uint8_t, 1);
1877 BHNDB_IO_METHODS(uint16_t, 2);
1878 BHNDB_IO_METHODS(uint32_t, 4);
1879 
1880 /**
1881  * Default bhndb(4) implementation of BHND_BUS_BARRIER().
1882  */
1883 static void
bhndb_bus_barrier(device_t dev,device_t child,struct bhnd_resource * r,bus_size_t offset,bus_size_t length,int flags)1884 bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
1885     bus_size_t offset, bus_size_t length, int flags)
1886 {
1887 	BHNDB_IO_COMMON_SETUP(length);
1888 
1889 	bus_barrier(io_res, io_offset + offset, length, flags);
1890 
1891 	BHNDB_IO_COMMON_TEARDOWN();
1892 }
1893 
1894 /**
1895  * Default bhndb(4) implementation of BHND_MAP_INTR().
1896  */
1897 static int
bhndb_bhnd_map_intr(device_t dev,device_t child,u_int intr,rman_res_t * irq)1898 bhndb_bhnd_map_intr(device_t dev, device_t child, u_int intr, rman_res_t *irq)
1899 {
1900 	u_int			 ivec;
1901 	int			 error;
1902 
1903 	/* Is the intr valid? */
1904 	if (intr >= bhnd_get_intr_count(child))
1905 		return (EINVAL);
1906 
1907 	/* Fetch the interrupt vector */
1908 	if ((error = bhnd_get_intr_ivec(child, intr, &ivec)))
1909 		return (error);
1910 
1911 	/* Map directly to the actual backplane interrupt vector */
1912 	*irq = ivec;
1913 
1914 	return (0);
1915 }
1916 
1917 /**
1918  * Default bhndb(4) implementation of BHND_UNMAP_INTR().
1919  */
1920 static void
bhndb_bhnd_unmap_intr(device_t dev,device_t child,rman_res_t irq)1921 bhndb_bhnd_unmap_intr(device_t dev, device_t child, rman_res_t irq)
1922 {
1923 	/* No state to clean up */
1924 }
1925 
1926 /**
1927  * Default bhndb(4) implementation of BUS_SETUP_INTR().
1928  */
1929 static int
bhndb_setup_intr(device_t dev,device_t child,struct resource * r,int flags,driver_filter_t filter,driver_intr_t handler,void * arg,void ** cookiep)1930 bhndb_setup_intr(device_t dev, device_t child, struct resource *r,
1931     int flags, driver_filter_t filter, driver_intr_t handler, void *arg,
1932     void **cookiep)
1933 {
1934 	struct bhndb_softc		*sc;
1935 	struct bhndb_intr_isrc		*isrc;
1936 	struct bhndb_intr_handler	*ih;
1937 	int				 error;
1938 
1939 	sc = device_get_softc(dev);
1940 
1941 	/* Fetch the isrc */
1942 	if ((error = BHNDB_MAP_INTR_ISRC(dev, r, &isrc))) {
1943 		device_printf(dev, "failed to fetch isrc: %d\n", error);
1944 		return (error);
1945 	}
1946 
1947 	/* Allocate new ihandler entry  */
1948 	ih = bhndb_alloc_intr_handler(child, r, isrc);
1949 	if (ih == NULL)
1950 		return (ENOMEM);
1951 
1952 	/* Perform actual interrupt setup via the host isrc */
1953 	error = bus_setup_intr(isrc->is_owner, isrc->is_res, flags, filter,
1954 	    handler, arg, &ih->ih_cookiep);
1955 	if (error) {
1956 		bhndb_free_intr_handler(ih);
1957 		return (error);
1958 	}
1959 
1960 	/* Add to our interrupt handler list */
1961 	BHNDB_LOCK(sc);
1962 	bhndb_register_intr_handler(sc->bus_res, ih);
1963 	BHNDB_UNLOCK(sc);
1964 
1965 	/* Provide the interrupt handler entry as our cookiep value */
1966 	*cookiep = ih;
1967 	return (0);
1968 }
1969 
1970 /**
1971  * Default bhndb(4) implementation of BUS_TEARDOWN_INTR().
1972  */
1973 static int
bhndb_teardown_intr(device_t dev,device_t child,struct resource * r,void * cookiep)1974 bhndb_teardown_intr(device_t dev, device_t child, struct resource *r,
1975     void *cookiep)
1976 {
1977 	struct bhndb_softc		*sc;
1978 	struct bhndb_intr_handler	*ih;
1979 	struct bhndb_intr_isrc		*isrc;
1980 	int				 error;
1981 
1982 	sc = device_get_softc(dev);
1983 
1984 	/* Locate and claim ownership of the interrupt handler entry */
1985 	BHNDB_LOCK(sc);
1986 
1987 	ih = bhndb_find_intr_handler(sc->bus_res, cookiep);
1988 	if (ih == NULL) {
1989 		panic("%s requested teardown of invalid cookiep %p",
1990 		    device_get_nameunit(child), cookiep);
1991 	}
1992 
1993 	bhndb_deregister_intr_handler(sc->bus_res, ih);
1994 
1995 	BHNDB_UNLOCK(sc);
1996 
1997 	/* Perform actual interrupt teardown via the host isrc */
1998 	isrc = ih->ih_isrc;
1999 	error = bus_teardown_intr(isrc->is_owner, isrc->is_res, ih->ih_cookiep);
2000 	if (error) {
2001 		/* If teardown fails, we need to reinsert the handler entry
2002 		 * to allow later teardown */
2003 		BHNDB_LOCK(sc);
2004 		bhndb_register_intr_handler(sc->bus_res, ih);
2005 		BHNDB_UNLOCK(sc);
2006 
2007 		return (error);
2008 	}
2009 
2010 	/* Free the entry */
2011 	bhndb_free_intr_handler(ih);
2012 	return (0);
2013 }
2014 
2015 /**
2016  * Default bhndb(4) implementation of BUS_BIND_INTR().
2017  */
2018 static int
bhndb_bind_intr(device_t dev,device_t child,struct resource * irq,int cpu)2019 bhndb_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
2020 {
2021 	struct bhndb_softc		*sc;
2022 	struct bhndb_intr_handler	*ih;
2023 	struct bhndb_intr_isrc		*isrc;
2024 
2025 	sc = device_get_softc(dev);
2026 	isrc = NULL;
2027 
2028 	/* Fetch the isrc corresponding to the child IRQ resource */
2029 	BHNDB_LOCK(sc);
2030 	STAILQ_FOREACH(ih, &sc->bus_res->bus_intrs, ih_link) {
2031 		if (ih->ih_res == irq) {
2032 			isrc = ih->ih_isrc;
2033 			break;
2034 		}
2035 	}
2036 	BHNDB_UNLOCK(sc);
2037 
2038 	if (isrc == NULL) {
2039 		panic("%s requested bind of invalid irq %#jx-%#jx",
2040 		    device_get_nameunit(child), rman_get_start(irq),
2041 		    rman_get_end(irq));
2042 	}
2043 
2044 	/* Perform actual bind via the host isrc */
2045 	return (bus_bind_intr(isrc->is_owner, isrc->is_res, cpu));
2046 }
2047 
2048 /**
2049  * Default bhndb(4) implementation of BUS_DESCRIBE_INTR().
2050  */
2051 static int
bhndb_describe_intr(device_t dev,device_t child,struct resource * irq,void * cookie,const char * descr)2052 bhndb_describe_intr(device_t dev, device_t child, struct resource *irq,
2053     void *cookie, const char *descr)
2054 {
2055 	struct bhndb_softc		*sc;
2056 	struct bhndb_intr_handler	*ih;
2057 	struct bhndb_intr_isrc		*isrc;
2058 
2059 	sc = device_get_softc(dev);
2060 
2061 	/* Locate the interrupt handler entry; the caller owns the handler
2062 	 * reference, and thus our entry is guaranteed to remain valid after
2063 	 * we drop out lock below. */
2064 	BHNDB_LOCK(sc);
2065 
2066 	ih = bhndb_find_intr_handler(sc->bus_res, cookie);
2067 	if (ih == NULL) {
2068 		panic("%s requested invalid cookiep %p",
2069 		    device_get_nameunit(child), cookie);
2070 	}
2071 
2072 	isrc = ih->ih_isrc;
2073 
2074 	BHNDB_UNLOCK(sc);
2075 
2076 	/* Perform the actual request via the host isrc */
2077 	return (BUS_DESCRIBE_INTR(device_get_parent(isrc->is_owner),
2078 	    isrc->is_owner, isrc->is_res, ih->ih_cookiep, descr));
2079 }
2080 
2081 /**
2082  * Default bhndb(4) implementation of BUS_CONFIG_INTR().
2083  */
2084 static int
bhndb_config_intr(device_t dev,int irq,enum intr_trigger trig,enum intr_polarity pol)2085 bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig,
2086     enum intr_polarity pol)
2087 {
2088 	/* Unsupported */
2089 	return (ENXIO);
2090 }
2091 
2092 /**
2093  * Default bhndb(4) implementation of BUS_REMAP_INTR().
2094  */
2095 static int
bhndb_remap_intr(device_t dev,device_t child,u_int irq)2096 bhndb_remap_intr(device_t dev, device_t child, u_int irq)
2097 {
2098 	/* Unsupported */
2099 	return (ENXIO);
2100 }
2101 
2102 /**
2103  * Default bhndb(4) implementation of BHND_BUS_GET_DMA_TRANSLATION().
2104  */
2105 static inline int
bhndb_get_dma_translation(device_t dev,device_t child,u_int width,uint32_t flags,bus_dma_tag_t * dmat,struct bhnd_dma_translation * translation)2106 bhndb_get_dma_translation(device_t dev, device_t child, u_int width,
2107     uint32_t flags, bus_dma_tag_t *dmat,
2108     struct bhnd_dma_translation *translation)
2109 {
2110 	struct bhndb_softc			*sc;
2111 	const struct bhndb_hwcfg		*hwcfg;
2112 	const struct bhnd_dma_translation	*match;
2113 	bus_dma_tag_t				 match_dmat;
2114 	bhnd_addr_t				 addr_mask, match_addr_mask;
2115 
2116 	sc = device_get_softc(dev);
2117 	hwcfg = sc->bus_res->cfg;
2118 
2119 	/* Is DMA supported? */
2120 	if (sc->bus_res->res->dma_tags == NULL)
2121 		return (ENODEV);
2122 
2123 	/* Is the requested width supported? */
2124 	if (width > BHND_DMA_ADDR_32BIT) {
2125 		/* Backplane must support 64-bit addressing */
2126 		if (!(sc->chipid.chip_caps & BHND_CAP_BP64))
2127 			width = BHND_DMA_ADDR_32BIT;
2128 	}
2129 
2130 	/* Find the best matching descriptor for the requested width */
2131 	addr_mask = BHND_DMA_ADDR_BITMASK(width);
2132 
2133 	match = NULL;
2134 	match_addr_mask = 0x0;
2135 	match_dmat = NULL;
2136 
2137 	for (size_t i = 0; i < sc->bus_res->res->num_dma_tags; i++) {
2138 		const struct bhnd_dma_translation	*dwin;
2139 		bhnd_addr_t				 masked;
2140 
2141 		dwin = &hwcfg->dma_translations[i];
2142 
2143 		/* The base address must be device addressable */
2144 		if ((dwin->base_addr & addr_mask) != dwin->base_addr)
2145 			continue;
2146 
2147 		/* The flags must match */
2148 		if ((dwin->flags & flags) != flags)
2149 			continue;
2150 
2151 		/* The window must cover at least part of our addressable
2152 		 * range */
2153 		masked = (dwin->addr_mask | dwin->addrext_mask) & addr_mask;
2154 		if (masked == 0)
2155 			continue;
2156 
2157 		/* Is this a better match? */
2158 		if (match == NULL || masked > match_addr_mask) {
2159 			match = dwin;
2160 			match_addr_mask = masked;
2161 			match_dmat = sc->bus_res->res->dma_tags[i];
2162 		}
2163 	}
2164 
2165 	if (match == NULL || match_addr_mask == 0)
2166 		return (ENOENT);
2167 
2168 	if (dmat != NULL)
2169 		*dmat = match_dmat;
2170 
2171 	if (translation != NULL)
2172 		*translation = *match;
2173 
2174 	return (0);
2175 }
2176 
2177 /**
2178  * Default bhndb(4) implementation of BUS_GET_DMA_TAG().
2179  */
2180 static bus_dma_tag_t
bhndb_get_dma_tag(device_t dev,device_t child)2181 bhndb_get_dma_tag(device_t dev, device_t child)
2182 {
2183 	struct bhndb_softc *sc = device_get_softc(dev);
2184 
2185 	/*
2186 	 * A bridge may have multiple DMA translation descriptors, each with
2187 	 * their own incompatible restrictions; drivers should in general call
2188 	 * BHND_BUS_GET_DMA_TRANSLATION() to fetch both the best available DMA
2189 	 * translation, and its corresponding DMA tag.
2190 	 *
2191 	 * Child drivers that do not use BHND_BUS_GET_DMA_TRANSLATION() are
2192 	 * responsible for creating their own restricted DMA tag; since we
2193 	 * cannot do this for them in BUS_GET_DMA_TAG(), we simply return the
2194 	 * bridge parent's DMA tag directly;
2195 	 */
2196 	return (bus_get_dma_tag(sc->parent_dev));
2197 }
2198 
2199 static device_method_t bhndb_methods[] = {
2200 	/* Device interface */ \
2201 	DEVMETHOD(device_probe,			bhndb_generic_probe),
2202 	DEVMETHOD(device_detach,		bhndb_generic_detach),
2203 	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
2204 	DEVMETHOD(device_suspend,		bhndb_generic_suspend),
2205 	DEVMETHOD(device_resume,		bhndb_generic_resume),
2206 
2207 	/* Bus interface */
2208 	DEVMETHOD(bus_probe_nomatch,		bhndb_probe_nomatch),
2209 	DEVMETHOD(bus_print_child,		bhndb_print_child),
2210 	DEVMETHOD(bus_child_pnpinfo_str,	bhndb_child_pnpinfo_str),
2211 	DEVMETHOD(bus_child_location_str,	bhndb_child_location_str),
2212 	DEVMETHOD(bus_add_child,		bhndb_add_child),
2213 	DEVMETHOD(bus_child_deleted,		bhndb_child_deleted),
2214 
2215 	DEVMETHOD(bus_alloc_resource,		bhndb_alloc_resource),
2216 	DEVMETHOD(bus_release_resource,		bhndb_release_resource),
2217 	DEVMETHOD(bus_activate_resource,	bhndb_activate_resource),
2218 	DEVMETHOD(bus_deactivate_resource,	bhndb_deactivate_resource),
2219 
2220 	DEVMETHOD(bus_setup_intr,		bhndb_setup_intr),
2221 	DEVMETHOD(bus_teardown_intr,		bhndb_teardown_intr),
2222 	DEVMETHOD(bus_config_intr,		bhndb_config_intr),
2223 	DEVMETHOD(bus_bind_intr,		bhndb_bind_intr),
2224 	DEVMETHOD(bus_describe_intr,		bhndb_describe_intr),
2225 	DEVMETHOD(bus_remap_intr,		bhndb_remap_intr),
2226 
2227 	DEVMETHOD(bus_get_dma_tag,		bhndb_get_dma_tag),
2228 
2229 	DEVMETHOD(bus_adjust_resource,		bhndb_adjust_resource),
2230 	DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
2231 	DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
2232 	DEVMETHOD(bus_delete_resource,		bus_generic_rl_delete_resource),
2233 	DEVMETHOD(bus_get_resource_list,	bhndb_get_resource_list),
2234 
2235 	DEVMETHOD(bus_read_ivar,		bhndb_read_ivar),
2236 	DEVMETHOD(bus_write_ivar,		bhndb_write_ivar),
2237 
2238 	/* BHNDB interface */
2239 	DEVMETHOD(bhndb_get_chipid,		bhndb_get_chipid),
2240 	DEVMETHOD(bhndb_is_core_disabled,	bhndb_is_core_disabled),
2241 	DEVMETHOD(bhndb_get_hostb_core,		bhndb_get_hostb_core),
2242 	DEVMETHOD(bhndb_suspend_resource,	bhndb_suspend_resource),
2243 	DEVMETHOD(bhndb_resume_resource,	bhndb_resume_resource),
2244 
2245 	/* BHND interface */
2246 	DEVMETHOD(bhnd_bus_get_chipid,		bhndb_get_chipid),
2247 	DEVMETHOD(bhnd_bus_activate_resource,	bhndb_activate_bhnd_resource),
2248 	DEVMETHOD(bhnd_bus_deactivate_resource,	bhndb_deactivate_bhnd_resource),
2249 	DEVMETHOD(bhnd_bus_get_nvram_var,	bhnd_bus_generic_get_nvram_var),
2250 	DEVMETHOD(bhnd_bus_map_intr,		bhndb_bhnd_map_intr),
2251 	DEVMETHOD(bhnd_bus_unmap_intr,		bhndb_bhnd_unmap_intr),
2252 	DEVMETHOD(bhnd_bus_get_dma_translation,	bhndb_get_dma_translation),
2253 
2254 	DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry),
2255 	DEVMETHOD(bhnd_bus_register_provider,	bhnd_bus_generic_sr_register_provider),
2256 	DEVMETHOD(bhnd_bus_deregister_provider,	bhnd_bus_generic_sr_deregister_provider),
2257 	DEVMETHOD(bhnd_bus_retain_provider,	bhnd_bus_generic_sr_retain_provider),
2258 	DEVMETHOD(bhnd_bus_release_provider,	bhnd_bus_generic_sr_release_provider),
2259 
2260 	DEVMETHOD(bhnd_bus_read_1,		bhndb_bus_read_1),
2261 	DEVMETHOD(bhnd_bus_read_2,		bhndb_bus_read_2),
2262 	DEVMETHOD(bhnd_bus_read_4,		bhndb_bus_read_4),
2263 	DEVMETHOD(bhnd_bus_write_1,		bhndb_bus_write_1),
2264 	DEVMETHOD(bhnd_bus_write_2,		bhndb_bus_write_2),
2265 	DEVMETHOD(bhnd_bus_write_4,		bhndb_bus_write_4),
2266 
2267 	DEVMETHOD(bhnd_bus_read_stream_1,	bhndb_bus_read_stream_1),
2268 	DEVMETHOD(bhnd_bus_read_stream_2,	bhndb_bus_read_stream_2),
2269 	DEVMETHOD(bhnd_bus_read_stream_4,	bhndb_bus_read_stream_4),
2270 	DEVMETHOD(bhnd_bus_write_stream_1,	bhndb_bus_write_stream_1),
2271 	DEVMETHOD(bhnd_bus_write_stream_2,	bhndb_bus_write_stream_2),
2272 	DEVMETHOD(bhnd_bus_write_stream_4,	bhndb_bus_write_stream_4),
2273 
2274 	DEVMETHOD(bhnd_bus_read_multi_1,	bhndb_bus_read_multi_1),
2275 	DEVMETHOD(bhnd_bus_read_multi_2,	bhndb_bus_read_multi_2),
2276 	DEVMETHOD(bhnd_bus_read_multi_4,	bhndb_bus_read_multi_4),
2277 	DEVMETHOD(bhnd_bus_write_multi_1,	bhndb_bus_write_multi_1),
2278 	DEVMETHOD(bhnd_bus_write_multi_2,	bhndb_bus_write_multi_2),
2279 	DEVMETHOD(bhnd_bus_write_multi_4,	bhndb_bus_write_multi_4),
2280 
2281 	DEVMETHOD(bhnd_bus_read_multi_stream_1,	bhndb_bus_read_multi_stream_1),
2282 	DEVMETHOD(bhnd_bus_read_multi_stream_2,	bhndb_bus_read_multi_stream_2),
2283 	DEVMETHOD(bhnd_bus_read_multi_stream_4,	bhndb_bus_read_multi_stream_4),
2284 	DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1),
2285 	DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2),
2286 	DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4),
2287 
2288 	DEVMETHOD(bhnd_bus_set_multi_1,		bhndb_bus_set_multi_1),
2289 	DEVMETHOD(bhnd_bus_set_multi_2,		bhndb_bus_set_multi_2),
2290 	DEVMETHOD(bhnd_bus_set_multi_4,		bhndb_bus_set_multi_4),
2291 	DEVMETHOD(bhnd_bus_set_region_1,	bhndb_bus_set_region_1),
2292 	DEVMETHOD(bhnd_bus_set_region_2,	bhndb_bus_set_region_2),
2293 	DEVMETHOD(bhnd_bus_set_region_4,	bhndb_bus_set_region_4),
2294 
2295 	DEVMETHOD(bhnd_bus_read_region_1,	bhndb_bus_read_region_1),
2296 	DEVMETHOD(bhnd_bus_read_region_2,	bhndb_bus_read_region_2),
2297 	DEVMETHOD(bhnd_bus_read_region_4,	bhndb_bus_read_region_4),
2298 	DEVMETHOD(bhnd_bus_write_region_1,	bhndb_bus_write_region_1),
2299 	DEVMETHOD(bhnd_bus_write_region_2,	bhndb_bus_write_region_2),
2300 	DEVMETHOD(bhnd_bus_write_region_4,	bhndb_bus_write_region_4),
2301 
2302 	DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1),
2303 	DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2),
2304 	DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4),
2305 	DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1),
2306 	DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2),
2307 	DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4),
2308 
2309 	DEVMETHOD(bhnd_bus_barrier,		bhndb_bus_barrier),
2310 
2311 	DEVMETHOD_END
2312 };
2313 
2314 devclass_t bhndb_devclass;
2315 
2316 DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc));
2317 
2318 MODULE_VERSION(bhndb, 1);
2319 MODULE_DEPEND(bhndb, bhnd, 1, 1, 1);
2320