xref: /freebsd/sys/dev/bhnd/bhnd.c (revision c94c8223bd444fa38ded3797060110c590f422f4)
1 /*-
2  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 /*
34  * Broadcom Home Networking Division (HND) Bus Driver.
35  *
36  * The Broadcom HND family of devices consists of both SoCs and host-connected
37  * networking chipsets containing a common family of Broadcom IP cores,
38  * including an integrated MIPS and/or ARM cores.
39  *
40  * HND devices expose a nearly identical interface whether accessible over a
41  * native SoC interconnect, or when connected via a host interface such as
42  * PCIe. As a result, the majority of hardware support code should be re-usable
43  * across host drivers for HND networking chipsets, as well as FreeBSD support
44  * for Broadcom MIPS/ARM HND SoCs.
45  *
46  * Earlier HND models used the siba(4) on-chip interconnect, while later models
47  * use bcma(4); the programming model is almost entirely independent
48  * of the actual underlying interconect.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/kernel.h>
53 #include <sys/bus.h>
54 #include <sys/module.h>
55 #include <sys/systm.h>
56 
57 #include <machine/bus.h>
58 #include <sys/rman.h>
59 #include <machine/resource.h>
60 
61 #include "bhnd.h"
62 #include "bhndvar.h"
63 
64 #include "bhnd_nvram_if.h"
65 
66 MALLOC_DEFINE(M_BHND, "bhnd", "bhnd bus data structures");
67 
68 /**
69  * bhnd_generic_probe_nomatch() reporting configuration.
70  */
71 static const struct bhnd_nomatch {
72 	uint16_t	vendor;		/**< core designer */
73 	uint16_t	device;		/**< core id */
74 	bool		if_verbose;	/**< print when bootverbose is set. */
75 } bhnd_nomatch_table[] = {
76 	{ BHND_MFGID_ARM,	BHND_COREID_OOB_ROUTER,		true	},
77 	{ BHND_MFGID_ARM,	BHND_COREID_EROM,		true	},
78 	{ BHND_MFGID_ARM,	BHND_COREID_PL301,		true	},
79 	{ BHND_MFGID_ARM,	BHND_COREID_APB_BRIDGE,		true	},
80 	{ BHND_MFGID_ARM,	BHND_COREID_AXI_UNMAPPED,	false	},
81 
82 	{ BHND_MFGID_INVALID,	BHND_COREID_INVALID,		false	}
83 };
84 
85 static device_t	find_nvram_child(device_t dev);
86 
87 static int	compare_ascending_probe_order(const void *lhs,
88 		    const void *rhs);
89 static int	compare_descending_probe_order(const void *lhs,
90 		    const void *rhs);
91 
92 /**
93  * Helper function for implementing DEVICE_ATTACH().
94  *
95  * This function can be used to implement DEVICE_ATTACH() for bhnd(4)
96  * bus implementations. It calls device_probe_and_attach() for each
97  * of the device's children, in order.
98  */
99 int
100 bhnd_generic_attach(device_t dev)
101 {
102 	device_t	*devs;
103 	int		 ndevs;
104 	int		 error;
105 
106 	if (device_is_attached(dev))
107 		return (EBUSY);
108 
109 	if ((error = device_get_children(dev, &devs, &ndevs)))
110 		return (error);
111 
112 	qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order);
113 	for (int i = 0; i < ndevs; i++) {
114 		device_t child = devs[i];
115 		device_probe_and_attach(child);
116 	}
117 
118 	free(devs, M_TEMP);
119 	return (0);
120 }
121 
122 /**
123  * Helper function for implementing DEVICE_DETACH().
124  *
125  * This function can be used to implement DEVICE_DETACH() for bhnd(4)
126  * bus implementations. It calls device_detach() for each
127  * of the device's children, in reverse order, terminating if
128  * any call to device_detach() fails.
129  */
130 int
131 bhnd_generic_detach(device_t dev)
132 {
133 	device_t	*devs;
134 	int		 ndevs;
135 	int		 error;
136 
137 	if (!device_is_attached(dev))
138 		return (EBUSY);
139 
140 	if ((error = device_get_children(dev, &devs, &ndevs)))
141 		return (error);
142 
143 	/* Detach in the reverse of attach order */
144 	qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
145 	for (int i = 0; i < ndevs; i++) {
146 		device_t child = devs[i];
147 
148 		/* Terminate on first error */
149 		if ((error = device_detach(child)))
150 			goto cleanup;
151 	}
152 
153 cleanup:
154 	free(devs, M_TEMP);
155 	return (error);
156 }
157 
158 /**
159  * Helper function for implementing DEVICE_SHUTDOWN().
160  *
161  * This function can be used to implement DEVICE_SHUTDOWN() for bhnd(4)
162  * bus implementations. It calls device_shutdown() for each
163  * of the device's children, in reverse order, terminating if
164  * any call to device_shutdown() fails.
165  */
166 int
167 bhnd_generic_shutdown(device_t dev)
168 {
169 	device_t	*devs;
170 	int		 ndevs;
171 	int		 error;
172 
173 	if (!device_is_attached(dev))
174 		return (EBUSY);
175 
176 	if ((error = device_get_children(dev, &devs, &ndevs)))
177 		return (error);
178 
179 	/* Shutdown in the reverse of attach order */
180 	qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
181 	for (int i = 0; i < ndevs; i++) {
182 		device_t child = devs[i];
183 
184 		/* Terminate on first error */
185 		if ((error = device_shutdown(child)))
186 			goto cleanup;
187 	}
188 
189 cleanup:
190 	free(devs, M_TEMP);
191 	return (error);
192 }
193 
194 /**
195  * Helper function for implementing DEVICE_RESUME().
196  *
197  * This function can be used to implement DEVICE_RESUME() for bhnd(4)
198  * bus implementations. It calls BUS_RESUME_CHILD() for each
199  * of the device's children, in order, terminating if
200  * any call to BUS_RESUME_CHILD() fails.
201  */
202 int
203 bhnd_generic_resume(device_t dev)
204 {
205 	device_t	*devs;
206 	int		 ndevs;
207 	int		 error;
208 
209 	if (!device_is_attached(dev))
210 		return (EBUSY);
211 
212 	if ((error = device_get_children(dev, &devs, &ndevs)))
213 		return (error);
214 
215 	qsort(devs, ndevs, sizeof(*devs), compare_ascending_probe_order);
216 	for (int i = 0; i < ndevs; i++) {
217 		device_t child = devs[i];
218 
219 		/* Terminate on first error */
220 		if ((error = BUS_RESUME_CHILD(device_get_parent(child), child)))
221 			goto cleanup;
222 	}
223 
224 cleanup:
225 	free(devs, M_TEMP);
226 	return (error);
227 }
228 
229 /**
230  * Helper function for implementing DEVICE_SUSPEND().
231  *
232  * This function can be used to implement DEVICE_SUSPEND() for bhnd(4)
233  * bus implementations. It calls BUS_SUSPEND_CHILD() for each
234  * of the device's children, in reverse order. If any call to
235  * BUS_SUSPEND_CHILD() fails, the suspend operation is terminated and
236  * any devices that were suspended are resumed immediately by calling
237  * their BUS_RESUME_CHILD() methods.
238  */
239 int
240 bhnd_generic_suspend(device_t dev)
241 {
242 	device_t	*devs;
243 	int		 ndevs;
244 	int		 error;
245 
246 	if (!device_is_attached(dev))
247 		return (EBUSY);
248 
249 	if ((error = device_get_children(dev, &devs, &ndevs)))
250 		return (error);
251 
252 	/* Suspend in the reverse of attach order */
253 	qsort(devs, ndevs, sizeof(*devs), compare_descending_probe_order);
254 	for (int i = 0; i < ndevs; i++) {
255 		device_t child = devs[i];
256 		error = BUS_SUSPEND_CHILD(device_get_parent(child), child);
257 
258 		/* On error, resume suspended devices and then terminate */
259 		if (error) {
260 			for (int j = 0; j < i; j++) {
261 				BUS_RESUME_CHILD(device_get_parent(devs[j]),
262 				    devs[j]);
263 			}
264 
265 			goto cleanup;
266 		}
267 	}
268 
269 cleanup:
270 	free(devs, M_TEMP);
271 	return (error);
272 }
273 
274 /*
275  * Ascending comparison of bhnd device's probe order.
276  */
277 static int
278 compare_ascending_probe_order(const void *lhs, const void *rhs)
279 {
280 	device_t	ldev, rdev;
281 	int		lorder, rorder;
282 
283 	ldev = (*(const device_t *) lhs);
284 	rdev = (*(const device_t *) rhs);
285 
286 	lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev);
287 	rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev);
288 
289 	if (lorder < rorder) {
290 		return (-1);
291 	} else if (lorder > rorder) {
292 		return (1);
293 	} else {
294 		return (0);
295 	}
296 }
297 
298 /*
299  * Descending comparison of bhnd device's probe order.
300  */
301 static int
302 compare_descending_probe_order(const void *lhs, const void *rhs)
303 {
304 	return (compare_ascending_probe_order(rhs, lhs));
305 }
306 
307 /**
308  * Helper function for implementing BHND_BUS_GET_PROBE_ORDER().
309  *
310  * This implementation determines probe ordering based on the device's class
311  * and other properties, including whether the device is serving as a host
312  * bridge.
313  */
314 int
315 bhnd_generic_get_probe_order(device_t dev, device_t child)
316 {
317 	switch (bhnd_get_class(child)) {
318 	case BHND_DEVCLASS_CC:
319 		return (BHND_PROBE_BUS + BHND_PROBE_ORDER_FIRST);
320 
321 	case BHND_DEVCLASS_CC_B:
322 		/* fall through */
323 	case BHND_DEVCLASS_PMU:
324 		return (BHND_PROBE_BUS + BHND_PROBE_ORDER_EARLY);
325 
326 	case BHND_DEVCLASS_SOC_ROUTER:
327 		return (BHND_PROBE_BUS + BHND_PROBE_ORDER_LATE);
328 
329 	case BHND_DEVCLASS_SOC_BRIDGE:
330 		return (BHND_PROBE_BUS + BHND_PROBE_ORDER_LAST);
331 
332 	case BHND_DEVCLASS_CPU:
333 		return (BHND_PROBE_CPU + BHND_PROBE_ORDER_FIRST);
334 
335 	case BHND_DEVCLASS_RAM:
336 		/* fall through */
337 	case BHND_DEVCLASS_MEMC:
338 		return (BHND_PROBE_CPU + BHND_PROBE_ORDER_EARLY);
339 
340 	case BHND_DEVCLASS_NVRAM:
341 		return (BHND_PROBE_RESOURCE + BHND_PROBE_ORDER_EARLY);
342 
343 	case BHND_DEVCLASS_PCI:
344 	case BHND_DEVCLASS_PCIE:
345 	case BHND_DEVCLASS_PCCARD:
346 	case BHND_DEVCLASS_ENET:
347 	case BHND_DEVCLASS_ENET_MAC:
348 	case BHND_DEVCLASS_ENET_PHY:
349 	case BHND_DEVCLASS_WLAN:
350 	case BHND_DEVCLASS_WLAN_MAC:
351 	case BHND_DEVCLASS_WLAN_PHY:
352 	case BHND_DEVCLASS_EROM:
353 	case BHND_DEVCLASS_OTHER:
354 	case BHND_DEVCLASS_INVALID:
355 		if (bhnd_is_hostb_device(child))
356 			return (BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY);
357 
358 		return (BHND_PROBE_DEFAULT);
359 	}
360 }
361 
362 /**
363  * Helper function for implementing BHND_BUS_IS_REGION_VALID().
364  *
365  * This implementation assumes that port and region numbers are 0-indexed and
366  * are allocated non-sparsely, using BHND_BUS_GET_PORT_COUNT() and
367  * BHND_BUS_GET_REGION_COUNT() to determine if @p port and @p region fall
368  * within the defined range.
369  */
370 bool
371 bhnd_generic_is_region_valid(device_t dev, device_t child,
372     bhnd_port_type type, u_int port, u_int region)
373 {
374 	if (port >= bhnd_get_port_count(child, type))
375 		return (false);
376 
377 	if (region >= bhnd_get_region_count(child, type, port))
378 		return (false);
379 
380 	return (true);
381 }
382 
383 /**
384  * Find an NVRAM child device on @p dev, if any.
385  *
386  * @retval device_t An NVRAM device.
387  * @retval NULL If no NVRAM device is found.
388  */
389 static device_t
390 find_nvram_child(device_t dev)
391 {
392 	device_t chipc, nvram;
393 
394 	/* Look for a directly-attached NVRAM child */
395 	nvram = device_find_child(dev, devclass_get_name(bhnd_nvram_devclass),
396 	    -1);
397 	if (nvram == NULL)
398 		return (NULL);
399 
400 	/* Further checks require a bhnd(4) bus */
401 	if (device_get_devclass(dev) != bhnd_devclass)
402 		return (NULL);
403 
404 	/* Look for a ChipCommon-attached OTP device */
405 	if ((chipc = bhnd_find_child(dev, BHND_DEVCLASS_CC, -1)) != NULL) {
406 		/* Recursively search the ChipCommon device */
407 		if ((nvram = find_nvram_child(chipc)) != NULL)
408 			return (nvram);
409 	}
410 
411 	/* Not found */
412 	return (NULL);
413 }
414 
415 /**
416  * Helper function for implementing BHND_BUS_READ_NVRAM_VAR().
417  *
418  * This implementation searches @p dev for a valid NVRAM device. If no NVRAM
419  * child device is found on @p dev, the request is delegated to the
420  * BHND_BUS_READ_NVRAM_VAR() method on the parent
421  * of @p dev.
422  */
423 int
424 bhnd_generic_read_nvram_var(device_t dev, device_t child, const char *name,
425     void *buf, size_t *size)
426 {
427 	device_t nvram;
428 
429 	/* Try to find an NVRAM device applicable to @p child */
430 	if ((nvram = find_nvram_child(dev)) == NULL)
431 		return (BHND_BUS_READ_NVRAM_VAR(device_get_parent(dev), child,
432 		    name, buf, size));
433 
434 	return BHND_NVRAM_GETVAR(nvram, name, buf, size);
435 }
436 
437 /**
438  * Helper function for implementing BUS_PRINT_CHILD().
439  *
440  * This implementation requests the device's struct resource_list via
441  * BUS_GET_RESOURCE_LIST.
442  */
443 int
444 bhnd_generic_print_child(device_t dev, device_t child)
445 {
446 	struct resource_list	*rl;
447 	int			retval = 0;
448 
449 	retval += bus_print_child_header(dev, child);
450 
451 	rl = BUS_GET_RESOURCE_LIST(dev, child);
452 	if (rl != NULL) {
453 		retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
454 		    "%#jx");
455 	}
456 
457 	retval += printf(" at core %u", bhnd_get_core_index(child));
458 
459 	retval += bus_print_child_domain(dev, child);
460 	retval += bus_print_child_footer(dev, child);
461 
462 	return (retval);
463 }
464 
465 /**
466  * Helper function for implementing BUS_PRINT_CHILD().
467  *
468  * This implementation requests the device's struct resource_list via
469  * BUS_GET_RESOURCE_LIST.
470  */
471 void
472 bhnd_generic_probe_nomatch(device_t dev, device_t child)
473 {
474 	struct resource_list		*rl;
475 	const struct bhnd_nomatch	*nm;
476 	bool				 report;
477 
478 	/* Fetch reporting configuration for this device */
479 	report = true;
480 	for (nm = bhnd_nomatch_table; nm->device != BHND_COREID_INVALID; nm++) {
481 		if (nm->vendor != bhnd_get_vendor(child))
482 			continue;
483 
484 		if (nm->device != bhnd_get_device(child))
485 			continue;
486 
487 		report = false;
488 		if (bootverbose && nm->if_verbose)
489 			report = true;
490 		break;
491 	}
492 
493 	if (!report)
494 		return;
495 
496 	/* Print the non-matched device info */
497 	device_printf(dev, "<%s %s>", bhnd_get_vendor_name(child),
498 		bhnd_get_device_name(child));
499 
500 	rl = BUS_GET_RESOURCE_LIST(dev, child);
501 	if (rl != NULL)
502 		resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx");
503 
504 	printf(" at core %u (no driver attached)\n",
505 	    bhnd_get_core_index(child));
506 }
507 
508 /**
509  * Default implementation of BUS_CHILD_PNPINFO_STR().
510  */
511 static int
512 bhnd_child_pnpinfo_str(device_t dev, device_t child, char *buf,
513     size_t buflen)
514 {
515 	if (device_get_parent(child) != dev) {
516 		return (BUS_CHILD_PNPINFO_STR(device_get_parent(dev), child,
517 		    buf, buflen));
518 	}
519 
520 	snprintf(buf, buflen, "vendor=0x%hx device=0x%hx rev=0x%hhx",
521 	    bhnd_get_vendor(child), bhnd_get_device(child),
522 	    bhnd_get_hwrev(child));
523 
524 	return (0);
525 }
526 
527 /**
528  * Default implementation of implementing BUS_PRINT_CHILD().
529  */
530 static int
531 bhnd_child_location_str(device_t dev, device_t child, char *buf,
532     size_t buflen)
533 {
534 	bhnd_addr_t	addr;
535 	bhnd_size_t	size;
536 
537 	if (device_get_parent(child) != dev) {
538 		return (BUS_CHILD_LOCATION_STR(device_get_parent(dev), child,
539 		    buf, buflen));
540 	}
541 
542 
543 	if (bhnd_get_region_addr(child, BHND_PORT_DEVICE, 0, 0, &addr, &size)) {
544 		/* No device default port/region */
545 		if (buflen > 0)
546 			*buf = '\0';
547 		return (0);
548 	}
549 
550 	snprintf(buf, buflen, "port0.0=0x%llx", (unsigned long long) addr);
551 	return (0);
552 }
553 
554 /**
555  * Helper function for implementing BUS_SUSPEND_CHILD().
556  *
557  * TODO: Power management
558  *
559  * If @p child is not a direct child of @p dev, suspension is delegated to
560  * the @p dev parent.
561  */
562 int
563 bhnd_generic_suspend_child(device_t dev, device_t child)
564 {
565 	if (device_get_parent(child) != dev)
566 		BUS_SUSPEND_CHILD(device_get_parent(dev), child);
567 
568 	return bus_generic_suspend_child(dev, child);
569 }
570 
571 /**
572  * Helper function for implementing BUS_RESUME_CHILD().
573  *
574  * TODO: Power management
575  *
576  * If @p child is not a direct child of @p dev, suspension is delegated to
577  * the @p dev parent.
578  */
579 int
580 bhnd_generic_resume_child(device_t dev, device_t child)
581 {
582 	if (device_get_parent(child) != dev)
583 		BUS_RESUME_CHILD(device_get_parent(dev), child);
584 
585 	return bus_generic_resume_child(dev, child);
586 }
587 
588 /**
589  * Helper function for implementing BHND_BUS_IS_HOSTB_DEVICE().
590  *
591  * If a parent device is available, this implementation delegates the
592  * request to the BHND_BUS_IS_HOSTB_DEVICE() method on the parent of @p dev.
593  *
594  * If no parent device is available (i.e. on a the bus root), false
595  * is returned.
596  */
597 bool
598 bhnd_generic_is_hostb_device(device_t dev, device_t child) {
599 	if (device_get_parent(dev) != NULL)
600 		return (BHND_BUS_IS_HOSTB_DEVICE(device_get_parent(dev),
601 		    child));
602 
603 	return (false);
604 }
605 
606 /**
607  * Helper function for implementing BHND_BUS_IS_HW_DISABLED().
608  *
609  * If a parent device is available, this implementation delegates the
610  * request to the BHND_BUS_IS_HW_DISABLED() method on the parent of @p dev.
611  *
612  * If no parent device is available (i.e. on a the bus root), the hardware
613  * is assumed to be usable and false is returned.
614  */
615 bool
616 bhnd_generic_is_hw_disabled(device_t dev, device_t child)
617 {
618 	if (device_get_parent(dev) != NULL)
619 		return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child));
620 
621 	return (false);
622 }
623 
624 /**
625  * Helper function for implementing BHND_BUS_GET_CHIPID().
626  *
627  * This implementation delegates the request to the BHND_BUS_GET_CHIPID()
628  * method on the parent of @p dev.
629  */
630 const struct bhnd_chipid *
631 bhnd_generic_get_chipid(device_t dev, device_t child) {
632 	return (BHND_BUS_GET_CHIPID(device_get_parent(dev), child));
633 }
634 
635 /**
636  * Helper function for implementing BHND_BUS_ALLOC_RESOURCE().
637  *
638  * This simple implementation of BHND_BUS_ALLOC_RESOURCE() determines
639  * any default values via BUS_GET_RESOURCE_LIST(), and calls
640  * BHND_BUS_ALLOC_RESOURCE() method of the parent of @p dev.
641  *
642  * If no parent device is available, the request is instead delegated to
643  * BUS_ALLOC_RESOURCE().
644  */
645 struct bhnd_resource *
646 bhnd_generic_alloc_bhnd_resource(device_t dev, device_t child, int type,
647 	int *rid, rman_res_t start, rman_res_t end, rman_res_t count,
648 	u_int flags)
649 {
650 	struct bhnd_resource		*r;
651 	struct resource_list		*rl;
652 	struct resource_list_entry	*rle;
653 	bool				 isdefault;
654 	bool				 passthrough;
655 
656 	passthrough = (device_get_parent(child) != dev);
657 	isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
658 
659 	/* the default RID must always be the first device port/region. */
660 	if (!passthrough && *rid == 0) {
661 		int rid0 = bhnd_get_port_rid(child, BHND_PORT_DEVICE, 0, 0);
662 		KASSERT(*rid == rid0,
663 		    ("rid 0 does not map to the first device port (%d)", rid0));
664 	}
665 
666 	/* Determine locally-known defaults before delegating the request. */
667 	if (!passthrough && isdefault) {
668 		/* fetch resource list from child's bus */
669 		rl = BUS_GET_RESOURCE_LIST(dev, child);
670 		if (rl == NULL)
671 			return (NULL); /* no resource list */
672 
673 		/* look for matching type/rid pair */
674 		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
675 		    type, *rid);
676 		if (rle == NULL)
677 			return (NULL);
678 
679 		/* set default values */
680 		start = rle->start;
681 		end = rle->end;
682 		count = ulmax(count, rle->count);
683 	}
684 
685 	/* Try to delegate to our parent. */
686 	if (device_get_parent(dev) != NULL) {
687 		return (BHND_BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
688 		    type, rid, start, end, count, flags));
689 	}
690 
691 	/* If this is the bus root, use a real bus-allocated resource */
692 	r = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
693 	if (r == NULL)
694 		return NULL;
695 
696 	/* Allocate the bus resource, marking it as 'direct' (not requiring
697 	 * any bus window remapping to perform I/O) */
698 	r->direct = true;
699 	r->res = BUS_ALLOC_RESOURCE(dev, child, type, rid, start, end,
700 	    count, flags);
701 
702 	if (r->res == NULL) {
703 		free(r, M_BHND);
704 		return NULL;
705 	}
706 
707 	return (r);
708 }
709 
710 /**
711  * Helper function for implementing BHND_BUS_RELEASE_RESOURCE().
712  *
713  * This simple implementation of BHND_BUS_RELEASE_RESOURCE() simply calls the
714  * BHND_BUS_RELEASE_RESOURCE() method of the parent of @p dev.
715  *
716  * If no parent device is available, the request is delegated to
717  * BUS_RELEASE_RESOURCE().
718  */
719 int
720 bhnd_generic_release_bhnd_resource(device_t dev, device_t child, int type,
721     int rid, struct bhnd_resource *r)
722 {
723 	int error;
724 
725 	/* Try to delegate to the parent. */
726 	if (device_get_parent(dev) != NULL)
727 		return (BHND_BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
728 		    type, rid, r));
729 
730 	/* Release the resource directly */
731 	if (!r->direct) {
732 		panic("bhnd indirect resource released without "
733 		    "bhnd parent bus");
734 	}
735 
736 	error = BUS_RELEASE_RESOURCE(dev, child, type, rid, r->res);
737 	if (error)
738 		return (error);
739 
740 	free(r, M_BHND);
741 	return (0);
742 }
743 
744 /**
745  * Helper function for implementing BHND_BUS_ACTIVATE_RESOURCE().
746  *
747  * This simple implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the
748  * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev.
749  *
750  * If no parent device is available, the request is delegated to
751  * BUS_ACTIVATE_RESOURCE().
752  */
753 int
754 bhnd_generic_activate_bhnd_resource(device_t dev, device_t child, int type,
755 	int rid, struct bhnd_resource *r)
756 {
757 	/* Try to delegate to the parent */
758 	if (device_get_parent(dev) != NULL)
759 		return (BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev),
760 		    child, type, rid, r));
761 
762 	/* Activate the resource directly */
763 	if (!r->direct) {
764 		panic("bhnd indirect resource released without "
765 		    "bhnd parent bus");
766 	}
767 
768 	return (BUS_ACTIVATE_RESOURCE(dev, child, type, rid, r->res));
769 };
770 
771 /**
772  * Helper function for implementing BHND_BUS_DEACTIVATE_RESOURCE().
773  *
774  * This simple implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the
775  * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev.
776  *
777  * If no parent device is available, the request is delegated to
778  * BUS_DEACTIVATE_RESOURCE().
779  */
780 int
781 bhnd_generic_deactivate_bhnd_resource(device_t dev, device_t child, int type,
782 	int rid, struct bhnd_resource *r)
783 {
784 	if (device_get_parent(dev) != NULL)
785 		return (BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev),
786 		    child, type, rid, r));
787 
788 	/* De-activate the resource directly */
789 	if (!r->direct) {
790 		panic("bhnd indirect resource released without "
791 		    "bhnd parent bus");
792 	}
793 
794 	return (BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r->res));
795 };
796 
797 /*
798  * Delegate all indirect I/O to the parent device. When inherited by
799  * non-bridged bus implementations, resources will never be marked as
800  * indirect, and these methods should never be called.
801  */
802 
803 static uint8_t
804 bhnd_read_1(device_t dev, device_t child, struct bhnd_resource *r,
805     bus_size_t offset)
806 {
807 	return (BHND_BUS_READ_1(device_get_parent(dev), child, r, offset));
808 }
809 
810 static uint16_t
811 bhnd_read_2(device_t dev, device_t child, struct bhnd_resource *r,
812     bus_size_t offset)
813 {
814 	return (BHND_BUS_READ_2(device_get_parent(dev), child, r, offset));
815 }
816 
817 static uint32_t
818 bhnd_read_4(device_t dev, device_t child, struct bhnd_resource *r,
819     bus_size_t offset)
820 {
821 	return (BHND_BUS_READ_4(device_get_parent(dev), child, r, offset));
822 }
823 
824 static void
825 bhnd_write_1(device_t dev, device_t child, struct bhnd_resource *r,
826     bus_size_t offset, uint8_t value)
827 {
828 	BHND_BUS_WRITE_1(device_get_parent(dev), child, r, offset, value);
829 }
830 
831 static void
832 bhnd_write_2(device_t dev, device_t child, struct bhnd_resource *r,
833     bus_size_t offset, uint16_t value)
834 {
835 	BHND_BUS_WRITE_2(device_get_parent(dev), child, r, offset, value);
836 }
837 
838 static void
839 bhnd_write_4(device_t dev, device_t child, struct bhnd_resource *r,
840     bus_size_t offset, uint32_t value)
841 {
842 	BHND_BUS_WRITE_4(device_get_parent(dev), child, r, offset, value);
843 }
844 
845 static void
846 bhnd_barrier(device_t dev, device_t child, struct bhnd_resource *r,
847     bus_size_t offset, bus_size_t length, int flags)
848 {
849 	BHND_BUS_BARRIER(device_get_parent(dev), child, r, offset, length,
850 	    flags);
851 }
852 
853 static device_method_t bhnd_methods[] = {
854 	/* Device interface */ \
855 	DEVMETHOD(device_attach,		bhnd_generic_attach),
856 	DEVMETHOD(device_detach,		bhnd_generic_detach),
857 	DEVMETHOD(device_shutdown,		bhnd_generic_shutdown),
858 	DEVMETHOD(device_suspend,		bhnd_generic_suspend),
859 	DEVMETHOD(device_resume,		bhnd_generic_resume),
860 
861 	/* Bus interface */
862 	DEVMETHOD(bus_probe_nomatch,		bhnd_generic_probe_nomatch),
863 	DEVMETHOD(bus_print_child,		bhnd_generic_print_child),
864 	DEVMETHOD(bus_child_pnpinfo_str,	bhnd_child_pnpinfo_str),
865 	DEVMETHOD(bus_child_location_str,	bhnd_child_location_str),
866 
867 	DEVMETHOD(bus_suspend_child,		bhnd_generic_suspend_child),
868 	DEVMETHOD(bus_resume_child,		bhnd_generic_resume_child),
869 
870 	DEVMETHOD(bus_set_resource,		bus_generic_rl_set_resource),
871 	DEVMETHOD(bus_get_resource,		bus_generic_rl_get_resource),
872 	DEVMETHOD(bus_delete_resource,		bus_generic_rl_delete_resource),
873 	DEVMETHOD(bus_alloc_resource,		bus_generic_rl_alloc_resource),
874 	DEVMETHOD(bus_adjust_resource,		bus_generic_adjust_resource),
875 	DEVMETHOD(bus_release_resource,		bus_generic_rl_release_resource),
876 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
877 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
878 
879 	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
880 	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
881 	DEVMETHOD(bus_config_intr,		bus_generic_config_intr),
882 	DEVMETHOD(bus_bind_intr,		bus_generic_bind_intr),
883 	DEVMETHOD(bus_describe_intr,		bus_generic_describe_intr),
884 
885 	DEVMETHOD(bus_get_dma_tag,		bus_generic_get_dma_tag),
886 
887 	/* BHND interface */
888 	DEVMETHOD(bhnd_bus_alloc_resource,	bhnd_generic_alloc_bhnd_resource),
889 	DEVMETHOD(bhnd_bus_release_resource,	bhnd_generic_release_bhnd_resource),
890 	DEVMETHOD(bhnd_bus_activate_resource,	bhnd_generic_activate_bhnd_resource),
891 	DEVMETHOD(bhnd_bus_activate_resource,	bhnd_generic_deactivate_bhnd_resource),
892 	DEVMETHOD(bhnd_bus_get_chipid,		bhnd_generic_get_chipid),
893 	DEVMETHOD(bhnd_bus_get_probe_order,	bhnd_generic_get_probe_order),
894 	DEVMETHOD(bhnd_bus_read_1,		bhnd_read_1),
895 	DEVMETHOD(bhnd_bus_read_2,		bhnd_read_2),
896 	DEVMETHOD(bhnd_bus_read_4,		bhnd_read_4),
897 	DEVMETHOD(bhnd_bus_write_1,		bhnd_write_1),
898 	DEVMETHOD(bhnd_bus_write_2,		bhnd_write_2),
899 	DEVMETHOD(bhnd_bus_write_4,		bhnd_write_4),
900 	DEVMETHOD(bhnd_bus_barrier,		bhnd_barrier),
901 
902 	DEVMETHOD_END
903 };
904 
905 devclass_t bhnd_devclass;	/**< bhnd bus. */
906 devclass_t bhnd_hostb_devclass;	/**< bhnd bus host bridge. */
907 devclass_t bhnd_nvram_devclass;	/**< bhnd NVRAM device */
908 
909 DEFINE_CLASS_0(bhnd, bhnd_driver, bhnd_methods, sizeof(struct bhnd_softc));
910 MODULE_VERSION(bhnd, 1);
911