xref: /freebsd-src/sys/dev/bhnd/bhnd_subr.c (revision a866a40b9b8095a1a31eb22d575535c1f5fbc080)
14ad7e9b0SAdrian Chadd /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
36e778a7eSPedro F. Giffuni  *
48e35bf83SLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
58e35bf83SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
64ad7e9b0SAdrian Chadd  * All rights reserved.
74ad7e9b0SAdrian Chadd  *
88e35bf83SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
98e35bf83SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
108e35bf83SLandon J. Fuller  *
114ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
124ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
134ad7e9b0SAdrian Chadd  * are met:
144ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
154ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
164ad7e9b0SAdrian Chadd  *    without modification.
174ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
184ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
194ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
204ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
214ad7e9b0SAdrian Chadd  *
224ad7e9b0SAdrian Chadd  * NO WARRANTY
234ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
244ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
254ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
264ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
274ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
284ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
294ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
304ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
314ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
324ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
334ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
344ad7e9b0SAdrian Chadd  */
354ad7e9b0SAdrian Chadd 
3696546b75SAdrian Chadd #include <sys/param.h>
374ad7e9b0SAdrian Chadd #include <sys/bus.h>
388e35bf83SLandon J. Fuller #include <sys/refcount.h>
394ad7e9b0SAdrian Chadd #include <sys/systm.h>
404ad7e9b0SAdrian Chadd 
414ad7e9b0SAdrian Chadd #include <machine/bus.h>
424ad7e9b0SAdrian Chadd #include <sys/rman.h>
434ad7e9b0SAdrian Chadd #include <machine/resource.h>
444ad7e9b0SAdrian Chadd 
45d851916eSLandon J. Fuller #include <dev/bhnd/siba/sibareg.h>
46d851916eSLandon J. Fuller 
474ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/chipc/chipcreg.h>
484ad7e9b0SAdrian Chadd 
49d567592bSAdrian Chadd #include "nvram/bhnd_nvram.h"
50d567592bSAdrian Chadd 
51d567592bSAdrian Chadd #include "bhnd_chipc_if.h"
52d567592bSAdrian Chadd 
53d567592bSAdrian Chadd #include "bhnd_nvram_if.h"
54d567592bSAdrian Chadd #include "bhnd_nvram_map.h"
55d567592bSAdrian Chadd 
564ad7e9b0SAdrian Chadd #include "bhndreg.h"
574ad7e9b0SAdrian Chadd #include "bhndvar.h"
588e35bf83SLandon J. Fuller #include "bhnd_private.h"
598e35bf83SLandon J. Fuller 
608e35bf83SLandon J. Fuller static void	bhnd_service_registry_free_entry(
618e35bf83SLandon J. Fuller 		    struct bhnd_service_entry *entry);
628e35bf83SLandon J. Fuller 
638e35bf83SLandon J. Fuller static int	compare_ascending_probe_order(const void *lhs, const void *rhs);
648e35bf83SLandon J. Fuller static int	compare_descending_probe_order(const void *lhs,
658e35bf83SLandon J. Fuller 		    const void *rhs);
664ad7e9b0SAdrian Chadd 
674ad7e9b0SAdrian Chadd /* BHND core device description table. */
684ad7e9b0SAdrian Chadd static const struct bhnd_core_desc {
694ad7e9b0SAdrian Chadd 	uint16_t	 vendor;
704ad7e9b0SAdrian Chadd 	uint16_t	 device;
714ad7e9b0SAdrian Chadd 	bhnd_devclass_t	 class;
724ad7e9b0SAdrian Chadd 	const char	*desc;
734ad7e9b0SAdrian Chadd } bhnd_core_descs[] = {
744ad7e9b0SAdrian Chadd 	#define	BHND_CDESC(_mfg, _cid, _cls, _desc)		\
754ad7e9b0SAdrian Chadd 	    { BHND_MFGID_ ## _mfg, BHND_COREID_ ## _cid,	\
764ad7e9b0SAdrian Chadd 		BHND_DEVCLASS_ ## _cls, _desc }
774ad7e9b0SAdrian Chadd 
784ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, CC,		CC,		"ChipCommon I/O Controller"),
794ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ILINE20,	OTHER,		"iLine20 HPNA"),
804ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SRAM,		RAM,		"SRAM"),
814ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SDRAM,		RAM,		"SDRAM"),
824ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PCI,		PCI,		"PCI Bridge"),
83f75615f2SLandon J. Fuller 	BHND_CDESC(BCM, MIPS,		CPU,		"BMIPS CPU"),
844ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ENET,		ENET_MAC,	"Fast Ethernet MAC"),
85a225321fSLandon J. Fuller 	BHND_CDESC(BCM, V90_CODEC,	SOFTMODEM,	"V.90 SoftModem Codec"),
86fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, USB,		USB_DUAL,	"USB 1.1 Device/Host Controller"),
874ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ADSL,		OTHER,		"ADSL Core"),
884ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ILINE100,	OTHER,		"iLine100 HPNA"),
894ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, IPSEC,		OTHER,		"IPsec Accelerator"),
904ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, UTOPIA,		OTHER,		"UTOPIA ATM Core"),
914ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PCMCIA,		PCCARD,		"PCMCIA Bridge"),
924ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SOCRAM,		RAM,		"Internal Memory"),
934ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, MEMC,		MEMC,		"MEMC SDRAM Controller"),
944ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, OFDM,		OTHER,		"OFDM PHY"),
954ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, EXTIF,		OTHER,		"External Interface"),
964ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, D11,		WLAN,		"802.11 MAC/PHY/Radio"),
974ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, APHY,		WLAN_PHY,	"802.11a PHY"),
984ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, BPHY,		WLAN_PHY,	"802.11b PHY"),
994ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, GPHY,		WLAN_PHY,	"802.11g PHY"),
100f75615f2SLandon J. Fuller 	BHND_CDESC(BCM, MIPS33,		CPU,		"BMIPS33 CPU"),
101fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, USB11H,		USB_HOST,	"USB 1.1 Host Controller"),
102fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, USB11D,		USB_DEV,	"USB 1.1 Device Controller"),
103fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, USB20H,		USB_HOST,	"USB 2.0 Host Controller"),
104fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, USB20D,		USB_DEV,	"USB 2.0 Device Controller"),
1054ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SDIOH,		OTHER,		"SDIO Host Controller"),
1064ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ROBO,		OTHER,		"RoboSwitch"),
1074ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ATA100,		OTHER,		"Parallel ATA Controller"),
1084ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SATAXOR,	OTHER,		"SATA DMA/XOR Controller"),
1094ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, GIGETH,		ENET_MAC,	"Gigabit Ethernet MAC"),
1104ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PCIE,		PCIE,		"PCIe Bridge"),
1114ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NPHY,		WLAN_PHY,	"802.11n 2x2 PHY"),
1124ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SRAMC,		MEMC,		"SRAM Controller"),
1134ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, MINIMAC,	OTHER,		"MINI MAC/PHY"),
1144ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ARM11,		CPU,		"ARM1176 CPU"),
1154ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ARM7S,		CPU,		"ARM7TDMI-S CPU"),
1164ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, LPPHY,		WLAN_PHY,	"802.11a/b/g PHY"),
1174ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PMU,		PMU,		"PMU"),
1184ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SSNPHY,		WLAN_PHY,	"802.11n Single-Stream PHY"),
1194ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SDIOD,		OTHER,		"SDIO Device Core"),
1204ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, ARMCM3,		CPU,		"ARM Cortex-M3 CPU"),
1214ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, HTPHY,		WLAN_PHY,	"802.11n 4x4 PHY"),
122d342b2e9SLandon J. Fuller 	BHND_CDESC(MIPS,MIPS74K,	CPU,		"MIPS74k CPU"),
1234ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, GMAC,		ENET_MAC,	"Gigabit MAC core"),
1244ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, DMEMC,		MEMC,		"DDR1/DDR2 Memory Controller"),
1254ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PCIERC,		OTHER,		"PCIe Root Complex"),
1264ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, OCP,		SOC_BRIDGE,	"OCP to OCP Bridge"),
1274ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SC,		OTHER,		"Shared Common Core"),
1284ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, AHB,		SOC_BRIDGE,	"OCP to AHB Bridge"),
1294ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, SPIH,		OTHER,		"SPI Host Controller"),
1304ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, I2S,		OTHER,		"I2S Digital Audio Interface"),
1314ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, DMEMS,		MEMC,		"SDR/DDR1 Memory Controller"),
1324ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, UBUS_SHIM,	OTHER,		"BCM6362/UBUS WLAN SHIM"),
1334ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, PCIE2,		PCIE,		"PCIe Bridge (Gen2)"),
1344ad7e9b0SAdrian Chadd 
1354ad7e9b0SAdrian Chadd 	BHND_CDESC(ARM, APB_BRIDGE,	SOC_BRIDGE,	"BP135 AMBA3 AXI to APB Bridge"),
1364ad7e9b0SAdrian Chadd 	BHND_CDESC(ARM, PL301,		SOC_ROUTER,	"PL301 AMBA3 Interconnect"),
1374ad7e9b0SAdrian Chadd 	BHND_CDESC(ARM, EROM,		EROM,		"PL366 Device Enumeration ROM"),
1384ad7e9b0SAdrian Chadd 	BHND_CDESC(ARM, OOB_ROUTER,	OTHER,		"PL367 OOB Interrupt Router"),
1394ad7e9b0SAdrian Chadd 	BHND_CDESC(ARM, AXI_UNMAPPED,	OTHER,		"Unmapped Address Ranges"),
1404ad7e9b0SAdrian Chadd 
1414ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, 4706_CC,	CC,		"ChipCommon I/O Controller"),
1424ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_PCIE2,	PCIE,		"PCIe Bridge (Gen2)"),
1434ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_DMA,		OTHER,		"DMA engine"),
1444ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_SDIO,	OTHER,		"SDIO 3.0 Host Controller"),
145fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, NS_USB20H,	USB_HOST,	"USB 2.0 Host Controller"),
146fb88110cSLandon J. Fuller 	BHND_CDESC(BCM, NS_USB30H,	USB_HOST,	"USB 3.0 Host Controller"),
1474ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_A9JTAG,	OTHER,		"ARM Cortex A9 JTAG Interface"),
1484ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_DDR23_MEMC,	MEMC,		"Denali DDR2/DD3 Memory Controller"),
1494ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_ROM,		NVRAM,		"System ROM"),
1504ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_NAND,	NVRAM,		"NAND Flash Controller"),
1514ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_QSPI,	NVRAM,		"QSPI Flash Controller"),
1524ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, NS_CC_B,	CC_B,		"ChipCommon B Auxiliary I/O Controller"),
1534ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, 4706_SOCRAM,	RAM,		"Internal Memory"),
1544ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, IHOST_ARMCA9,	CPU,		"ARM Cortex A9 CPU"),
1554ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, 4706_GMAC_CMN,	ENET,		"Gigabit MAC (Common)"),
1564ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, 4706_GMAC,	ENET_MAC,	"Gigabit MAC"),
1574ad7e9b0SAdrian Chadd 	BHND_CDESC(BCM, AMEMC,		MEMC,		"Denali DDR1/DDR2 Memory Controller"),
1584ad7e9b0SAdrian Chadd #undef	BHND_CDESC
1594ad7e9b0SAdrian Chadd 
1604ad7e9b0SAdrian Chadd 	/* Derived from inspection of the BCM4331 cores that provide PrimeCell
1614ad7e9b0SAdrian Chadd 	 * IDs. Due to lack of documentation, the surmised device name/purpose
1624ad7e9b0SAdrian Chadd 	 * provided here may be incorrect. */
1634ad7e9b0SAdrian Chadd 	{ BHND_MFGID_ARM,	BHND_PRIMEID_EROM,	BHND_DEVCLASS_OTHER,
1644ad7e9b0SAdrian Chadd 	    "PL364 Device Enumeration ROM" },
1654ad7e9b0SAdrian Chadd 	{ BHND_MFGID_ARM,	BHND_PRIMEID_SWRAP,	BHND_DEVCLASS_OTHER,
1664ad7e9b0SAdrian Chadd 	    "PL368 Device Management Interface" },
1674ad7e9b0SAdrian Chadd 	{ BHND_MFGID_ARM,	BHND_PRIMEID_MWRAP,	BHND_DEVCLASS_OTHER,
1684ad7e9b0SAdrian Chadd 	    "PL369 Device Management Interface" },
1694ad7e9b0SAdrian Chadd 	{ 0, 0, 0, NULL }
1704ad7e9b0SAdrian Chadd };
1714ad7e9b0SAdrian Chadd 
1724e96bf3aSLandon J. Fuller static const struct bhnd_device_quirk bhnd_chipc_clkctl_quirks[];
1734e96bf3aSLandon J. Fuller static const struct bhnd_device_quirk bhnd_pcmcia_clkctl_quirks[];
1744e96bf3aSLandon J. Fuller 
1754e96bf3aSLandon J. Fuller /**
1764e96bf3aSLandon J. Fuller  * Device table entries for core-specific CLKCTL quirk lookup.
1774e96bf3aSLandon J. Fuller  */
1784e96bf3aSLandon J. Fuller static const struct bhnd_device bhnd_clkctl_devices[] = {
1794e96bf3aSLandon J. Fuller 	BHND_DEVICE(BCM, CC,		NULL,	bhnd_chipc_clkctl_quirks),
1804e96bf3aSLandon J. Fuller 	BHND_DEVICE(BCM, PCMCIA,	NULL,	bhnd_pcmcia_clkctl_quirks),
1814e96bf3aSLandon J. Fuller 	BHND_DEVICE_END,
1824e96bf3aSLandon J. Fuller };
1834e96bf3aSLandon J. Fuller 
1844e96bf3aSLandon J. Fuller /** ChipCommon CLKCTL quirks */
1854e96bf3aSLandon J. Fuller static const struct bhnd_device_quirk bhnd_chipc_clkctl_quirks[] = {
1864e96bf3aSLandon J. Fuller 	/* HTAVAIL/ALPAVAIL are bitswapped in chipc's CLKCTL */
1874e96bf3aSLandon J. Fuller 	BHND_CHIP_QUIRK(4328,	HWREV_ANY,	BHND_CLKCTL_QUIRK_CCS0),
1884e96bf3aSLandon J. Fuller 	BHND_CHIP_QUIRK(5354,	HWREV_ANY,	BHND_CLKCTL_QUIRK_CCS0),
1894e96bf3aSLandon J. Fuller 	BHND_DEVICE_QUIRK_END
1904e96bf3aSLandon J. Fuller };
1914e96bf3aSLandon J. Fuller 
1924e96bf3aSLandon J. Fuller /** PCMCIA CLKCTL quirks */
1934e96bf3aSLandon J. Fuller static const struct bhnd_device_quirk bhnd_pcmcia_clkctl_quirks[] = {
1944e96bf3aSLandon J. Fuller 	/* HTAVAIL/ALPAVAIL are bitswapped in pcmcia's CLKCTL */
1954e96bf3aSLandon J. Fuller 	BHND_CHIP_QUIRK(4328,	HWREV_ANY,	BHND_CLKCTL_QUIRK_CCS0),
1964e96bf3aSLandon J. Fuller 	BHND_CHIP_QUIRK(5354,	HWREV_ANY,	BHND_CLKCTL_QUIRK_CCS0),
1974e96bf3aSLandon J. Fuller 	BHND_DEVICE_QUIRK_END
1984e96bf3aSLandon J. Fuller };
1994e96bf3aSLandon J. Fuller 
2004ad7e9b0SAdrian Chadd /**
2014ad7e9b0SAdrian Chadd  * Return the name for a given JEP106 manufacturer ID.
2024ad7e9b0SAdrian Chadd  *
2034ad7e9b0SAdrian Chadd  * @param vendor A JEP106 Manufacturer ID, including the non-standard ARM 4-bit
2044ad7e9b0SAdrian Chadd  * JEP106 continuation code.
2054ad7e9b0SAdrian Chadd  */
2064ad7e9b0SAdrian Chadd const char *
bhnd_vendor_name(uint16_t vendor)2074ad7e9b0SAdrian Chadd bhnd_vendor_name(uint16_t vendor)
2084ad7e9b0SAdrian Chadd {
2094ad7e9b0SAdrian Chadd 	switch (vendor) {
2104ad7e9b0SAdrian Chadd 	case BHND_MFGID_ARM:
2114ad7e9b0SAdrian Chadd 		return "ARM";
2124ad7e9b0SAdrian Chadd 	case BHND_MFGID_BCM:
2134ad7e9b0SAdrian Chadd 		return "Broadcom";
2144ad7e9b0SAdrian Chadd 	case BHND_MFGID_MIPS:
2154ad7e9b0SAdrian Chadd 		return "MIPS";
2164ad7e9b0SAdrian Chadd 	default:
2174ad7e9b0SAdrian Chadd 		return "unknown";
2184ad7e9b0SAdrian Chadd 	}
2194ad7e9b0SAdrian Chadd }
2204ad7e9b0SAdrian Chadd 
2214ad7e9b0SAdrian Chadd /**
2224ad7e9b0SAdrian Chadd  * Return the name of a port type.
22305ed3f90SLandon J. Fuller  *
22405ed3f90SLandon J. Fuller  * @param port_type The port type to look up.
2254ad7e9b0SAdrian Chadd  */
2264ad7e9b0SAdrian Chadd const char *
bhnd_port_type_name(bhnd_port_type port_type)2274ad7e9b0SAdrian Chadd bhnd_port_type_name(bhnd_port_type port_type)
2284ad7e9b0SAdrian Chadd {
2294ad7e9b0SAdrian Chadd 	switch (port_type) {
2304ad7e9b0SAdrian Chadd 	case BHND_PORT_DEVICE:
2314ad7e9b0SAdrian Chadd 		return ("device");
2324ad7e9b0SAdrian Chadd 	case BHND_PORT_BRIDGE:
2334ad7e9b0SAdrian Chadd 		return ("bridge");
2344ad7e9b0SAdrian Chadd 	case BHND_PORT_AGENT:
2354ad7e9b0SAdrian Chadd 		return ("agent");
236c2ec037fSEd Maste 	default:
237c2ec037fSEd Maste 		return "unknown";
2384ad7e9b0SAdrian Chadd 	}
2394ad7e9b0SAdrian Chadd }
2404ad7e9b0SAdrian Chadd 
241fdedcd9fSLandon J. Fuller /**
242fdedcd9fSLandon J. Fuller  * Return the name of an NVRAM source.
24305ed3f90SLandon J. Fuller  *
24405ed3f90SLandon J. Fuller  * @param nvram_src The NVRAM source type to look up.
245fdedcd9fSLandon J. Fuller  */
246fdedcd9fSLandon J. Fuller const char *
bhnd_nvram_src_name(bhnd_nvram_src nvram_src)247fdedcd9fSLandon J. Fuller bhnd_nvram_src_name(bhnd_nvram_src nvram_src)
248fdedcd9fSLandon J. Fuller {
249fdedcd9fSLandon J. Fuller 	switch (nvram_src) {
250fdedcd9fSLandon J. Fuller 	case BHND_NVRAM_SRC_FLASH:
251fdedcd9fSLandon J. Fuller 		return ("flash");
252fdedcd9fSLandon J. Fuller 	case BHND_NVRAM_SRC_OTP:
253fdedcd9fSLandon J. Fuller 		return ("OTP");
254fdedcd9fSLandon J. Fuller 	case BHND_NVRAM_SRC_SPROM:
255fdedcd9fSLandon J. Fuller 		return ("SPROM");
256fdedcd9fSLandon J. Fuller 	case BHND_NVRAM_SRC_UNKNOWN:
257fdedcd9fSLandon J. Fuller 		return ("none");
258fdedcd9fSLandon J. Fuller 	default:
259fdedcd9fSLandon J. Fuller 		return ("unknown");
260fdedcd9fSLandon J. Fuller 	}
261fdedcd9fSLandon J. Fuller }
2624ad7e9b0SAdrian Chadd 
2634ad7e9b0SAdrian Chadd static const struct bhnd_core_desc *
bhnd_find_core_desc(uint16_t vendor,uint16_t device)2644ad7e9b0SAdrian Chadd bhnd_find_core_desc(uint16_t vendor, uint16_t device)
2654ad7e9b0SAdrian Chadd {
2664ad7e9b0SAdrian Chadd 	for (u_int i = 0; bhnd_core_descs[i].desc != NULL; i++) {
2674ad7e9b0SAdrian Chadd 		if (bhnd_core_descs[i].vendor != vendor)
2684ad7e9b0SAdrian Chadd 			continue;
2694ad7e9b0SAdrian Chadd 
2704ad7e9b0SAdrian Chadd 		if (bhnd_core_descs[i].device != device)
2714ad7e9b0SAdrian Chadd 			continue;
2724ad7e9b0SAdrian Chadd 
2734ad7e9b0SAdrian Chadd 		return (&bhnd_core_descs[i]);
2744ad7e9b0SAdrian Chadd 	}
2754ad7e9b0SAdrian Chadd 
2764ad7e9b0SAdrian Chadd 	return (NULL);
2774ad7e9b0SAdrian Chadd }
2784ad7e9b0SAdrian Chadd 
2794ad7e9b0SAdrian Chadd /**
2804ad7e9b0SAdrian Chadd  * Return a human-readable name for a BHND core.
2814ad7e9b0SAdrian Chadd  *
28205ed3f90SLandon J. Fuller  * @param vendor The core designer's JEDEC-106 Manufacturer ID.
2834ad7e9b0SAdrian Chadd  * @param device The core identifier.
2844ad7e9b0SAdrian Chadd  */
2854ad7e9b0SAdrian Chadd const char *
bhnd_find_core_name(uint16_t vendor,uint16_t device)2864ad7e9b0SAdrian Chadd bhnd_find_core_name(uint16_t vendor, uint16_t device)
2874ad7e9b0SAdrian Chadd {
2884ad7e9b0SAdrian Chadd 	const struct bhnd_core_desc *desc;
2894ad7e9b0SAdrian Chadd 
2904ad7e9b0SAdrian Chadd 	if ((desc = bhnd_find_core_desc(vendor, device)) == NULL)
2914ad7e9b0SAdrian Chadd 		return ("unknown");
2924ad7e9b0SAdrian Chadd 
2934ad7e9b0SAdrian Chadd 	return desc->desc;
2944ad7e9b0SAdrian Chadd }
2954ad7e9b0SAdrian Chadd 
2964ad7e9b0SAdrian Chadd /**
2974ad7e9b0SAdrian Chadd  * Return the device class for a BHND core.
2984ad7e9b0SAdrian Chadd  *
29905ed3f90SLandon J. Fuller  * @param vendor The core designer's JEDEC-106 Manufacturer ID.
3004ad7e9b0SAdrian Chadd  * @param device The core identifier.
3014ad7e9b0SAdrian Chadd  */
3024ad7e9b0SAdrian Chadd bhnd_devclass_t
bhnd_find_core_class(uint16_t vendor,uint16_t device)3034ad7e9b0SAdrian Chadd bhnd_find_core_class(uint16_t vendor, uint16_t device)
3044ad7e9b0SAdrian Chadd {
3054ad7e9b0SAdrian Chadd 	const struct bhnd_core_desc *desc;
3064ad7e9b0SAdrian Chadd 
3074ad7e9b0SAdrian Chadd 	if ((desc = bhnd_find_core_desc(vendor, device)) == NULL)
3084ad7e9b0SAdrian Chadd 		return (BHND_DEVCLASS_OTHER);
3094ad7e9b0SAdrian Chadd 
3104ad7e9b0SAdrian Chadd 	return desc->class;
3114ad7e9b0SAdrian Chadd }
3124ad7e9b0SAdrian Chadd 
3134ad7e9b0SAdrian Chadd /**
3144ad7e9b0SAdrian Chadd  * Return a human-readable name for a BHND core.
3154ad7e9b0SAdrian Chadd  *
3164ad7e9b0SAdrian Chadd  * @param ci The core's info record.
3174ad7e9b0SAdrian Chadd  */
3184ad7e9b0SAdrian Chadd const char *
bhnd_core_name(const struct bhnd_core_info * ci)3194ad7e9b0SAdrian Chadd bhnd_core_name(const struct bhnd_core_info *ci)
3204ad7e9b0SAdrian Chadd {
3214ad7e9b0SAdrian Chadd 	return bhnd_find_core_name(ci->vendor, ci->device);
3224ad7e9b0SAdrian Chadd }
3234ad7e9b0SAdrian Chadd 
3244ad7e9b0SAdrian Chadd /**
3254ad7e9b0SAdrian Chadd  * Return the device class for a BHND core.
3264ad7e9b0SAdrian Chadd  *
3274ad7e9b0SAdrian Chadd  * @param ci The core's info record.
3284ad7e9b0SAdrian Chadd  */
3294ad7e9b0SAdrian Chadd bhnd_devclass_t
bhnd_core_class(const struct bhnd_core_info * ci)3304ad7e9b0SAdrian Chadd bhnd_core_class(const struct bhnd_core_info *ci)
3314ad7e9b0SAdrian Chadd {
3324ad7e9b0SAdrian Chadd 	return bhnd_find_core_class(ci->vendor, ci->device);
3334ad7e9b0SAdrian Chadd }
3344ad7e9b0SAdrian Chadd 
3354ad7e9b0SAdrian Chadd /**
3369bb95906SLandon J. Fuller  * Write a human readable name representation of the given
3379bb95906SLandon J. Fuller  * BHND_CHIPID_* constant to @p buffer.
3389bb95906SLandon J. Fuller  *
3399bb95906SLandon J. Fuller  * @param buffer Output buffer, or NULL to compute the required size.
3409bb95906SLandon J. Fuller  * @param size Capacity of @p buffer, in bytes.
3419bb95906SLandon J. Fuller  * @param chip_id Chip ID to be formatted.
3429bb95906SLandon J. Fuller  *
34305ed3f90SLandon J. Fuller  * @return The required number of bytes on success, or a negative integer on
34405ed3f90SLandon J. Fuller  * failure. No more than @p size-1 characters be written, with the @p size'th
34505ed3f90SLandon J. Fuller  * set to '\0'.
3469bb95906SLandon J. Fuller  *
3479bb95906SLandon J. Fuller  * @sa BHND_CHIPID_MAX_NAMELEN
3489bb95906SLandon J. Fuller  */
3499bb95906SLandon J. Fuller int
bhnd_format_chip_id(char * buffer,size_t size,uint16_t chip_id)3509bb95906SLandon J. Fuller bhnd_format_chip_id(char *buffer, size_t size, uint16_t chip_id)
3519bb95906SLandon J. Fuller {
3529bb95906SLandon J. Fuller 	/* All hex formatted IDs are within the range of 0x4000-0x9C3F (40000-1) */
3539bb95906SLandon J. Fuller 	if (chip_id >= 0x4000 && chip_id <= 0x9C3F)
3549bb95906SLandon J. Fuller 		return (snprintf(buffer, size, "BCM%hX", chip_id));
3559bb95906SLandon J. Fuller 	else
3569bb95906SLandon J. Fuller 		return (snprintf(buffer, size, "BCM%hu", chip_id));
3579bb95906SLandon J. Fuller }
3589bb95906SLandon J. Fuller 
3599bb95906SLandon J. Fuller /**
36005ed3f90SLandon J. Fuller  * Return a core info record populated from a bhnd-attached @p dev.
3614ad7e9b0SAdrian Chadd  *
3624ad7e9b0SAdrian Chadd  * @param dev A bhnd device.
36305ed3f90SLandon J. Fuller  *
36405ed3f90SLandon J. Fuller  * @return A core info record for @p dev.
3654ad7e9b0SAdrian Chadd  */
3664ad7e9b0SAdrian Chadd struct bhnd_core_info
bhnd_get_core_info(device_t dev)3674ad7e9b0SAdrian Chadd bhnd_get_core_info(device_t dev) {
3684ad7e9b0SAdrian Chadd 	return (struct bhnd_core_info) {
3694ad7e9b0SAdrian Chadd 		.vendor		= bhnd_get_vendor(dev),
3704ad7e9b0SAdrian Chadd 		.device		= bhnd_get_device(dev),
3714ad7e9b0SAdrian Chadd 		.hwrev		= bhnd_get_hwrev(dev),
3724ad7e9b0SAdrian Chadd 		.core_idx	= bhnd_get_core_index(dev),
3734ad7e9b0SAdrian Chadd 		.unit		= bhnd_get_core_unit(dev)
3744ad7e9b0SAdrian Chadd 	};
3754ad7e9b0SAdrian Chadd }
3764ad7e9b0SAdrian Chadd 
3774ad7e9b0SAdrian Chadd /**
3788e35bf83SLandon J. Fuller  * Find a @p class child device with @p unit on @p bus.
3794ad7e9b0SAdrian Chadd  *
3808e35bf83SLandon J. Fuller  * @param bus The bhnd-compatible bus to be searched.
3814ad7e9b0SAdrian Chadd  * @param class The device class to match on.
382fdedcd9fSLandon J. Fuller  * @param unit The core unit number; specify -1 to return the first match
3834ad7e9b0SAdrian Chadd  * regardless of unit number.
3844ad7e9b0SAdrian Chadd  *
3854ad7e9b0SAdrian Chadd  * @retval device_t if a matching child device is found.
3864ad7e9b0SAdrian Chadd  * @retval NULL if no matching child device is found.
3874ad7e9b0SAdrian Chadd  */
3884ad7e9b0SAdrian Chadd device_t
bhnd_bus_find_child(device_t bus,bhnd_devclass_t class,int unit)3898e35bf83SLandon J. Fuller bhnd_bus_find_child(device_t bus, bhnd_devclass_t class, int unit)
3904ad7e9b0SAdrian Chadd {
3914ad7e9b0SAdrian Chadd 	struct bhnd_core_match md = {
3925ad9ac03SAdrian Chadd 		BHND_MATCH_CORE_CLASS(class),
3935ad9ac03SAdrian Chadd 		BHND_MATCH_CORE_UNIT(unit)
3944ad7e9b0SAdrian Chadd 	};
3954ad7e9b0SAdrian Chadd 
3965ad9ac03SAdrian Chadd 	if (unit == -1)
3975ad9ac03SAdrian Chadd 		md.m.match.core_unit = 0;
3985ad9ac03SAdrian Chadd 
3998e35bf83SLandon J. Fuller 	return bhnd_bus_match_child(bus, &md);
4004ad7e9b0SAdrian Chadd }
4014ad7e9b0SAdrian Chadd 
4024ad7e9b0SAdrian Chadd /**
4038e35bf83SLandon J. Fuller  * Find the first child device on @p bus that matches @p desc.
4044ad7e9b0SAdrian Chadd  *
4058e35bf83SLandon J. Fuller  * @param bus The bhnd-compatible bus to be searched.
4064ad7e9b0SAdrian Chadd  * @param desc A match descriptor.
4074ad7e9b0SAdrian Chadd  *
4084ad7e9b0SAdrian Chadd  * @retval device_t if a matching child device is found.
4094ad7e9b0SAdrian Chadd  * @retval NULL if no matching child device is found.
4104ad7e9b0SAdrian Chadd  */
4114ad7e9b0SAdrian Chadd device_t
bhnd_bus_match_child(device_t bus,const struct bhnd_core_match * desc)4128e35bf83SLandon J. Fuller bhnd_bus_match_child(device_t bus, const struct bhnd_core_match *desc)
4134ad7e9b0SAdrian Chadd {
4144ad7e9b0SAdrian Chadd 	device_t	*devlistp;
4154ad7e9b0SAdrian Chadd 	device_t	 match;
4164ad7e9b0SAdrian Chadd 	int		 devcnt;
4174ad7e9b0SAdrian Chadd 	int		 error;
4184ad7e9b0SAdrian Chadd 
4198e35bf83SLandon J. Fuller 	error = device_get_children(bus, &devlistp, &devcnt);
4204ad7e9b0SAdrian Chadd 	if (error != 0)
4214ad7e9b0SAdrian Chadd 		return (NULL);
4224ad7e9b0SAdrian Chadd 
4234ad7e9b0SAdrian Chadd 	match = NULL;
4244ad7e9b0SAdrian Chadd 	for (int i = 0; i < devcnt; i++) {
4255ad9ac03SAdrian Chadd 		struct bhnd_core_info ci = bhnd_get_core_info(devlistp[i]);
4265ad9ac03SAdrian Chadd 
4275ad9ac03SAdrian Chadd 		if (bhnd_core_matches(&ci, desc)) {
4285ad9ac03SAdrian Chadd 			match = devlistp[i];
4294ad7e9b0SAdrian Chadd 			goto done;
4304ad7e9b0SAdrian Chadd 		}
4314ad7e9b0SAdrian Chadd 	}
4324ad7e9b0SAdrian Chadd 
4334ad7e9b0SAdrian Chadd done:
4344ad7e9b0SAdrian Chadd 	free(devlistp, M_TEMP);
4354ad7e9b0SAdrian Chadd 	return match;
4364ad7e9b0SAdrian Chadd }
4374ad7e9b0SAdrian Chadd 
4384ad7e9b0SAdrian Chadd /**
4398e35bf83SLandon J. Fuller  * Retrieve an ordered list of all device instances currently connected to
4408e35bf83SLandon J. Fuller  * @p bus, returning a pointer to the array in @p devlistp and the count
4418e35bf83SLandon J. Fuller  * in @p ndevs.
4428e35bf83SLandon J. Fuller  *
4438e35bf83SLandon J. Fuller  * The memory allocated for the table must be freed via
4448e35bf83SLandon J. Fuller  * bhnd_bus_free_children().
4458e35bf83SLandon J. Fuller  *
4468e35bf83SLandon J. Fuller  * @param	bus		The bhnd-compatible bus to be queried.
4478e35bf83SLandon J. Fuller  * @param[out]	devlist		The array of devices.
4488e35bf83SLandon J. Fuller  * @param[out]	devcount	The number of devices in @p devlistp
4498e35bf83SLandon J. Fuller  * @param	order		The order in which devices will be returned
4508e35bf83SLandon J. Fuller  *				in @p devlist.
4518e35bf83SLandon J. Fuller  *
4528e35bf83SLandon J. Fuller  * @retval 0		success
4538e35bf83SLandon J. Fuller  * @retval non-zero	if an error occurs, a regular unix error code will
4548e35bf83SLandon J. Fuller  *			be returned.
4558e35bf83SLandon J. Fuller  */
4568e35bf83SLandon J. Fuller int
bhnd_bus_get_children(device_t bus,device_t ** devlist,int * devcount,bhnd_device_order order)4578e35bf83SLandon J. Fuller bhnd_bus_get_children(device_t bus, device_t **devlist, int *devcount,
4588e35bf83SLandon J. Fuller     bhnd_device_order order)
4598e35bf83SLandon J. Fuller {
4608e35bf83SLandon J. Fuller 	int error;
4618e35bf83SLandon J. Fuller 
4628e35bf83SLandon J. Fuller 	/* Fetch device array */
4638e35bf83SLandon J. Fuller 	if ((error = device_get_children(bus, devlist, devcount)))
4648e35bf83SLandon J. Fuller 		return (error);
4658e35bf83SLandon J. Fuller 
4668e35bf83SLandon J. Fuller 	/* Perform requested sorting */
4678e35bf83SLandon J. Fuller 	if ((error = bhnd_sort_devices(*devlist, *devcount, order))) {
4688e35bf83SLandon J. Fuller 		bhnd_bus_free_children(*devlist);
4698e35bf83SLandon J. Fuller 		return (error);
4708e35bf83SLandon J. Fuller 	}
4718e35bf83SLandon J. Fuller 
4728e35bf83SLandon J. Fuller 	return (0);
4738e35bf83SLandon J. Fuller }
4748e35bf83SLandon J. Fuller 
4758e35bf83SLandon J. Fuller /**
4768e35bf83SLandon J. Fuller  * Free any memory allocated in a previous call to bhnd_bus_get_children().
4778e35bf83SLandon J. Fuller  *
4788e35bf83SLandon J. Fuller  * @param devlist The device array returned by bhnd_bus_get_children().
4798e35bf83SLandon J. Fuller  */
4808e35bf83SLandon J. Fuller void
bhnd_bus_free_children(device_t * devlist)4818e35bf83SLandon J. Fuller bhnd_bus_free_children(device_t *devlist)
4828e35bf83SLandon J. Fuller {
4838e35bf83SLandon J. Fuller 	free(devlist, M_TEMP);
4848e35bf83SLandon J. Fuller }
4858e35bf83SLandon J. Fuller 
4868e35bf83SLandon J. Fuller /**
4878e35bf83SLandon J. Fuller  * Perform in-place sorting of an array of bhnd device instances.
4888e35bf83SLandon J. Fuller  *
4898e35bf83SLandon J. Fuller  * @param devlist	An array of bhnd devices.
4908e35bf83SLandon J. Fuller  * @param devcount	The number of devices in @p devs.
4918e35bf83SLandon J. Fuller  * @param order		The sort order to be used.
49205ed3f90SLandon J. Fuller  *
49305ed3f90SLandon J. Fuller  * @retval 0		success
49405ed3f90SLandon J. Fuller  * @retval EINVAL	if the sort order is unknown.
4958e35bf83SLandon J. Fuller  */
4968e35bf83SLandon J. Fuller int
bhnd_sort_devices(device_t * devlist,size_t devcount,bhnd_device_order order)4978e35bf83SLandon J. Fuller bhnd_sort_devices(device_t *devlist, size_t devcount, bhnd_device_order order)
4988e35bf83SLandon J. Fuller {
4998e35bf83SLandon J. Fuller 	int (*compare)(const void *, const void *);
5008e35bf83SLandon J. Fuller 
5018e35bf83SLandon J. Fuller 	switch (order) {
5028e35bf83SLandon J. Fuller 	case BHND_DEVICE_ORDER_ATTACH:
5038e35bf83SLandon J. Fuller 		compare = compare_ascending_probe_order;
5048e35bf83SLandon J. Fuller 		break;
5058e35bf83SLandon J. Fuller 	case BHND_DEVICE_ORDER_DETACH:
5068e35bf83SLandon J. Fuller 		compare = compare_descending_probe_order;
5078e35bf83SLandon J. Fuller 		break;
5088e35bf83SLandon J. Fuller 	default:
5098e35bf83SLandon J. Fuller 		printf("unknown sort order: %d\n", order);
5108e35bf83SLandon J. Fuller 		return (EINVAL);
5118e35bf83SLandon J. Fuller 	}
5128e35bf83SLandon J. Fuller 
5138e35bf83SLandon J. Fuller 	qsort(devlist, devcount, sizeof(*devlist), compare);
5148e35bf83SLandon J. Fuller 	return (0);
5158e35bf83SLandon J. Fuller }
5168e35bf83SLandon J. Fuller 
5178e35bf83SLandon J. Fuller /*
5188e35bf83SLandon J. Fuller  * Ascending comparison of bhnd device's probe order.
5198e35bf83SLandon J. Fuller  */
5208e35bf83SLandon J. Fuller static int
compare_ascending_probe_order(const void * lhs,const void * rhs)5218e35bf83SLandon J. Fuller compare_ascending_probe_order(const void *lhs, const void *rhs)
5228e35bf83SLandon J. Fuller {
5238e35bf83SLandon J. Fuller 	device_t	ldev, rdev;
5248e35bf83SLandon J. Fuller 	int		lorder, rorder;
5258e35bf83SLandon J. Fuller 
5268e35bf83SLandon J. Fuller 	ldev = (*(const device_t *) lhs);
5278e35bf83SLandon J. Fuller 	rdev = (*(const device_t *) rhs);
5288e35bf83SLandon J. Fuller 
5298e35bf83SLandon J. Fuller 	lorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(ldev), ldev);
5308e35bf83SLandon J. Fuller 	rorder = BHND_BUS_GET_PROBE_ORDER(device_get_parent(rdev), rdev);
5318e35bf83SLandon J. Fuller 
5328e35bf83SLandon J. Fuller 	if (lorder < rorder) {
5338e35bf83SLandon J. Fuller 		return (-1);
5348e35bf83SLandon J. Fuller 	} else if (lorder > rorder) {
5358e35bf83SLandon J. Fuller 		return (1);
5368e35bf83SLandon J. Fuller 	} else {
5378e35bf83SLandon J. Fuller 		return (0);
5388e35bf83SLandon J. Fuller 	}
5398e35bf83SLandon J. Fuller }
5408e35bf83SLandon J. Fuller 
5418e35bf83SLandon J. Fuller /*
5428e35bf83SLandon J. Fuller  * Descending comparison of bhnd device's probe order.
5438e35bf83SLandon J. Fuller  */
5448e35bf83SLandon J. Fuller static int
compare_descending_probe_order(const void * lhs,const void * rhs)5458e35bf83SLandon J. Fuller compare_descending_probe_order(const void *lhs, const void *rhs)
5468e35bf83SLandon J. Fuller {
5478e35bf83SLandon J. Fuller 	return (compare_ascending_probe_order(rhs, lhs));
5488e35bf83SLandon J. Fuller }
5498e35bf83SLandon J. Fuller 
5508e35bf83SLandon J. Fuller /**
5518e35bf83SLandon J. Fuller  * Call device_probe_and_attach() for each of the bhnd bus device's
5528e35bf83SLandon J. Fuller  * children, in bhnd attach order.
5538e35bf83SLandon J. Fuller  *
5548e35bf83SLandon J. Fuller  * @param bus The bhnd-compatible bus for which all children should be probed
5558e35bf83SLandon J. Fuller  * and attached.
5568e35bf83SLandon J. Fuller  */
5578e35bf83SLandon J. Fuller int
bhnd_bus_probe_children(device_t bus)5588e35bf83SLandon J. Fuller bhnd_bus_probe_children(device_t bus)
5598e35bf83SLandon J. Fuller {
5608e35bf83SLandon J. Fuller 	device_t	*devs;
5618e35bf83SLandon J. Fuller 	int		 ndevs;
5628e35bf83SLandon J. Fuller 	int		 error;
5638e35bf83SLandon J. Fuller 
5648e35bf83SLandon J. Fuller 	/* Fetch children in attach order */
5658e35bf83SLandon J. Fuller 	error = bhnd_bus_get_children(bus, &devs, &ndevs,
5668e35bf83SLandon J. Fuller 	    BHND_DEVICE_ORDER_ATTACH);
5678e35bf83SLandon J. Fuller 	if (error)
5688e35bf83SLandon J. Fuller 		return (error);
5698e35bf83SLandon J. Fuller 
5708e35bf83SLandon J. Fuller 	/* Probe and attach all children */
5718e35bf83SLandon J. Fuller 	for (int i = 0; i < ndevs; i++) {
5728e35bf83SLandon J. Fuller 		device_t child = devs[i];
5738e35bf83SLandon J. Fuller 		device_probe_and_attach(child);
5748e35bf83SLandon J. Fuller 	}
5758e35bf83SLandon J. Fuller 
5768e35bf83SLandon J. Fuller 	bhnd_bus_free_children(devs);
5778e35bf83SLandon J. Fuller 
5788e35bf83SLandon J. Fuller 	return (0);
5798e35bf83SLandon J. Fuller }
5808e35bf83SLandon J. Fuller 
5818e35bf83SLandon J. Fuller /**
5828ef24a0dSAdrian Chadd  * Walk up the bhnd device hierarchy to locate the root device
5838ef24a0dSAdrian Chadd  * to which the bhndb bridge is attached.
5848ef24a0dSAdrian Chadd  *
5858ef24a0dSAdrian Chadd  * This can be used from within bhnd host bridge drivers to locate the
5868ef24a0dSAdrian Chadd  * actual upstream host device.
5878ef24a0dSAdrian Chadd  *
5888ef24a0dSAdrian Chadd  * @param dev A bhnd device.
5898ef24a0dSAdrian Chadd  * @param bus_class The expected bus (e.g. "pci") to which the bridge root
5908ef24a0dSAdrian Chadd  * should be attached.
5918ef24a0dSAdrian Chadd  *
5928ef24a0dSAdrian Chadd  * @retval device_t if a matching parent device is found.
59305ed3f90SLandon J. Fuller  * @retval NULL if @p dev is not attached via a bhndb bus.
59405ed3f90SLandon J. Fuller  * @retval NULL if no parent device is attached via @p bus_class.
5958ef24a0dSAdrian Chadd  */
5968ef24a0dSAdrian Chadd device_t
bhnd_find_bridge_root(device_t dev,devclass_t bus_class)5978ef24a0dSAdrian Chadd bhnd_find_bridge_root(device_t dev, devclass_t bus_class)
5988ef24a0dSAdrian Chadd {
5998ef24a0dSAdrian Chadd 	devclass_t	bhndb_class;
6008ef24a0dSAdrian Chadd 	device_t	parent;
6018ef24a0dSAdrian Chadd 
60258d83161SJohn Baldwin 	KASSERT(device_get_devclass(device_get_parent(dev)) ==
60358d83161SJohn Baldwin 	    devclass_find("bhnd"),
6048ef24a0dSAdrian Chadd 	   ("%s not a bhnd device", device_get_nameunit(dev)));
6058ef24a0dSAdrian Chadd 
6068ef24a0dSAdrian Chadd 	bhndb_class = devclass_find("bhndb");
6078ef24a0dSAdrian Chadd 
6088ef24a0dSAdrian Chadd 	/* Walk the device tree until we hit a bridge */
6098ef24a0dSAdrian Chadd 	parent = dev;
6108ef24a0dSAdrian Chadd 	while ((parent = device_get_parent(parent)) != NULL) {
6118ef24a0dSAdrian Chadd 		if (device_get_devclass(parent) == bhndb_class)
6128ef24a0dSAdrian Chadd 			break;
6138ef24a0dSAdrian Chadd 	}
6148ef24a0dSAdrian Chadd 
6158ef24a0dSAdrian Chadd 	/* No bridge? */
6168ef24a0dSAdrian Chadd 	if (parent == NULL)
6178ef24a0dSAdrian Chadd 		return (NULL);
6188ef24a0dSAdrian Chadd 
6198ef24a0dSAdrian Chadd 	/* Search for a parent attached to the expected bus class */
6208ef24a0dSAdrian Chadd 	while ((parent = device_get_parent(parent)) != NULL) {
6218ef24a0dSAdrian Chadd 		device_t bus;
6228ef24a0dSAdrian Chadd 
6238ef24a0dSAdrian Chadd 		bus = device_get_parent(parent);
6248ef24a0dSAdrian Chadd 		if (bus != NULL && device_get_devclass(bus) == bus_class)
6258ef24a0dSAdrian Chadd 			return (parent);
6268ef24a0dSAdrian Chadd 	}
6278ef24a0dSAdrian Chadd 
6288ef24a0dSAdrian Chadd 	/* Not found */
6298ef24a0dSAdrian Chadd 	return (NULL);
6308ef24a0dSAdrian Chadd }
6318ef24a0dSAdrian Chadd 
6328ef24a0dSAdrian Chadd /**
6334ad7e9b0SAdrian Chadd  * Find the first core in @p cores that matches @p desc.
6344ad7e9b0SAdrian Chadd  *
6354ad7e9b0SAdrian Chadd  * @param cores The table to search.
6364ad7e9b0SAdrian Chadd  * @param num_cores The length of @p cores.
6374ad7e9b0SAdrian Chadd  * @param desc A match descriptor.
6384ad7e9b0SAdrian Chadd  *
6394ad7e9b0SAdrian Chadd  * @retval bhnd_core_info if a matching core is found.
6404ad7e9b0SAdrian Chadd  * @retval NULL if no matching core is found.
6414ad7e9b0SAdrian Chadd  */
6424ad7e9b0SAdrian Chadd const struct bhnd_core_info *
bhnd_match_core(const struct bhnd_core_info * cores,u_int num_cores,const struct bhnd_core_match * desc)6434ad7e9b0SAdrian Chadd bhnd_match_core(const struct bhnd_core_info *cores, u_int num_cores,
6444ad7e9b0SAdrian Chadd     const struct bhnd_core_match *desc)
6454ad7e9b0SAdrian Chadd {
6464ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < num_cores; i++) {
6474ad7e9b0SAdrian Chadd 		if (bhnd_core_matches(&cores[i], desc))
6484ad7e9b0SAdrian Chadd 			return &cores[i];
6494ad7e9b0SAdrian Chadd 	}
6504ad7e9b0SAdrian Chadd 
6514ad7e9b0SAdrian Chadd 	return (NULL);
6524ad7e9b0SAdrian Chadd }
6534ad7e9b0SAdrian Chadd 
6544ad7e9b0SAdrian Chadd /**
6554ad7e9b0SAdrian Chadd  * Find the first core in @p cores with the given @p class.
6564ad7e9b0SAdrian Chadd  *
6574ad7e9b0SAdrian Chadd  * @param cores The table to search.
6584ad7e9b0SAdrian Chadd  * @param num_cores The length of @p cores.
65905ed3f90SLandon J. Fuller  * @param class The device class to match on.
6604ad7e9b0SAdrian Chadd  *
66105ed3f90SLandon J. Fuller  * @retval non-NULL if a matching core is found.
6624ad7e9b0SAdrian Chadd  * @retval NULL if no matching core is found.
6634ad7e9b0SAdrian Chadd  */
6644ad7e9b0SAdrian Chadd const struct bhnd_core_info *
bhnd_find_core(const struct bhnd_core_info * cores,u_int num_cores,bhnd_devclass_t class)6654ad7e9b0SAdrian Chadd bhnd_find_core(const struct bhnd_core_info *cores, u_int num_cores,
6664ad7e9b0SAdrian Chadd     bhnd_devclass_t class)
6674ad7e9b0SAdrian Chadd {
6684ad7e9b0SAdrian Chadd 	struct bhnd_core_match md = {
6695ad9ac03SAdrian Chadd 		BHND_MATCH_CORE_CLASS(class)
6704ad7e9b0SAdrian Chadd 	};
6714ad7e9b0SAdrian Chadd 
6724ad7e9b0SAdrian Chadd 	return bhnd_match_core(cores, num_cores, &md);
6734ad7e9b0SAdrian Chadd }
6744ad7e9b0SAdrian Chadd 
675111d7cb2SLandon J. Fuller /**
676111d7cb2SLandon J. Fuller  * Create an equality match descriptor for @p core.
677111d7cb2SLandon J. Fuller  *
678111d7cb2SLandon J. Fuller  * @param core The core info to be matched on.
67905ed3f90SLandon J. Fuller  *
68005ed3f90SLandon J. Fuller  * @return an equality match descriptor for @p core.
681111d7cb2SLandon J. Fuller  */
682111d7cb2SLandon J. Fuller struct bhnd_core_match
bhnd_core_get_match_desc(const struct bhnd_core_info * core)683111d7cb2SLandon J. Fuller bhnd_core_get_match_desc(const struct bhnd_core_info *core)
684111d7cb2SLandon J. Fuller {
685111d7cb2SLandon J. Fuller 	return ((struct bhnd_core_match) {
686111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_VENDOR(core->vendor),
687111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_ID(core->device),
688111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_REV(HWREV_EQ(core->hwrev)),
689111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_CLASS(bhnd_core_class(core)),
690111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_IDX(core->core_idx),
691111d7cb2SLandon J. Fuller 		BHND_MATCH_CORE_UNIT(core->unit)
692111d7cb2SLandon J. Fuller 	});
693111d7cb2SLandon J. Fuller }
694111d7cb2SLandon J. Fuller 
695111d7cb2SLandon J. Fuller /**
69605ed3f90SLandon J. Fuller  * Return true if the @p lhs is equal to @p rhs.
697111d7cb2SLandon J. Fuller  *
698111d7cb2SLandon J. Fuller  * @param lhs The first bhnd core descriptor to compare.
699111d7cb2SLandon J. Fuller  * @param rhs The second bhnd core descriptor to compare.
700111d7cb2SLandon J. Fuller  *
701111d7cb2SLandon J. Fuller  * @retval true if @p lhs is equal to @p rhs
702111d7cb2SLandon J. Fuller  * @retval false if @p lhs is not equal to @p rhs
703111d7cb2SLandon J. Fuller  */
704111d7cb2SLandon J. Fuller bool
bhnd_cores_equal(const struct bhnd_core_info * lhs,const struct bhnd_core_info * rhs)705111d7cb2SLandon J. Fuller bhnd_cores_equal(const struct bhnd_core_info *lhs,
706111d7cb2SLandon J. Fuller     const struct bhnd_core_info *rhs)
707111d7cb2SLandon J. Fuller {
708111d7cb2SLandon J. Fuller 	struct bhnd_core_match md;
709111d7cb2SLandon J. Fuller 
710111d7cb2SLandon J. Fuller 	/* Use an equality match descriptor to perform the comparison */
711111d7cb2SLandon J. Fuller 	md = bhnd_core_get_match_desc(rhs);
712111d7cb2SLandon J. Fuller 	return (bhnd_core_matches(lhs, &md));
713111d7cb2SLandon J. Fuller }
714111d7cb2SLandon J. Fuller 
7154ad7e9b0SAdrian Chadd /**
7164ad7e9b0SAdrian Chadd  * Return true if the @p core matches @p desc.
7174ad7e9b0SAdrian Chadd  *
7184ad7e9b0SAdrian Chadd  * @param core A bhnd core descriptor.
7194ad7e9b0SAdrian Chadd  * @param desc A match descriptor to compare against @p core.
7204ad7e9b0SAdrian Chadd  *
72105ed3f90SLandon J. Fuller  * @retval true if @p core matches @p match.
7224ad7e9b0SAdrian Chadd  * @retval false if @p core does not match @p match.
7234ad7e9b0SAdrian Chadd  */
7244ad7e9b0SAdrian Chadd bool
bhnd_core_matches(const struct bhnd_core_info * core,const struct bhnd_core_match * desc)7254ad7e9b0SAdrian Chadd bhnd_core_matches(const struct bhnd_core_info *core,
7264ad7e9b0SAdrian Chadd     const struct bhnd_core_match *desc)
7274ad7e9b0SAdrian Chadd {
7285ad9ac03SAdrian Chadd 	if (desc->m.match.core_vendor && desc->core_vendor != core->vendor)
7294ad7e9b0SAdrian Chadd 		return (false);
7304ad7e9b0SAdrian Chadd 
7315ad9ac03SAdrian Chadd 	if (desc->m.match.core_id && desc->core_id != core->device)
7324ad7e9b0SAdrian Chadd 		return (false);
7334ad7e9b0SAdrian Chadd 
7345ad9ac03SAdrian Chadd 	if (desc->m.match.core_unit && desc->core_unit != core->unit)
7354ad7e9b0SAdrian Chadd 		return (false);
7364ad7e9b0SAdrian Chadd 
7375ad9ac03SAdrian Chadd 	if (desc->m.match.core_rev &&
7385ad9ac03SAdrian Chadd 	    !bhnd_hwrev_matches(core->hwrev, &desc->core_rev))
7394ad7e9b0SAdrian Chadd 		return (false);
7404ad7e9b0SAdrian Chadd 
741111d7cb2SLandon J. Fuller 	if (desc->m.match.core_idx && desc->core_idx != core->core_idx)
742111d7cb2SLandon J. Fuller 		return (false);
743111d7cb2SLandon J. Fuller 
7445ad9ac03SAdrian Chadd 	if (desc->m.match.core_class &&
7455ad9ac03SAdrian Chadd 	    desc->core_class != bhnd_core_class(core))
7464ad7e9b0SAdrian Chadd 		return (false);
7474ad7e9b0SAdrian Chadd 
7484ad7e9b0SAdrian Chadd 	return true;
7494ad7e9b0SAdrian Chadd }
7504ad7e9b0SAdrian Chadd 
7514ad7e9b0SAdrian Chadd /**
752f74f1a68SAdrian Chadd  * Return true if the @p chip matches @p desc.
753f74f1a68SAdrian Chadd  *
754f74f1a68SAdrian Chadd  * @param chip A bhnd chip identifier.
755f74f1a68SAdrian Chadd  * @param desc A match descriptor to compare against @p chip.
756f74f1a68SAdrian Chadd  *
75705ed3f90SLandon J. Fuller  * @retval true if @p chip matches @p match.
758f74f1a68SAdrian Chadd  * @retval false if @p chip does not match @p match.
759f74f1a68SAdrian Chadd  */
760f74f1a68SAdrian Chadd bool
bhnd_chip_matches(const struct bhnd_chipid * chip,const struct bhnd_chip_match * desc)761f74f1a68SAdrian Chadd bhnd_chip_matches(const struct bhnd_chipid *chip,
762f74f1a68SAdrian Chadd     const struct bhnd_chip_match *desc)
763f74f1a68SAdrian Chadd {
7645ad9ac03SAdrian Chadd 	if (desc->m.match.chip_id && chip->chip_id != desc->chip_id)
765d567592bSAdrian Chadd 		return (false);
766d567592bSAdrian Chadd 
7675ad9ac03SAdrian Chadd 	if (desc->m.match.chip_pkg && chip->chip_pkg != desc->chip_pkg)
768f74f1a68SAdrian Chadd 		return (false);
769f74f1a68SAdrian Chadd 
7705ad9ac03SAdrian Chadd 	if (desc->m.match.chip_rev &&
771f74f1a68SAdrian Chadd 	    !bhnd_hwrev_matches(chip->chip_rev, &desc->chip_rev))
772f74f1a68SAdrian Chadd 		return (false);
773f74f1a68SAdrian Chadd 
774caeff9a3SLandon J. Fuller 	if (desc->m.match.chip_type && chip->chip_type != desc->chip_type)
775caeff9a3SLandon J. Fuller 		return (false);
776caeff9a3SLandon J. Fuller 
7775ad9ac03SAdrian Chadd 	return (true);
7785ad9ac03SAdrian Chadd }
779d567592bSAdrian Chadd 
7805ad9ac03SAdrian Chadd /**
7815ad9ac03SAdrian Chadd  * Return true if the @p board matches @p desc.
7825ad9ac03SAdrian Chadd  *
7835ad9ac03SAdrian Chadd  * @param board The bhnd board info.
7845ad9ac03SAdrian Chadd  * @param desc A match descriptor to compare against @p board.
7855ad9ac03SAdrian Chadd  *
78605ed3f90SLandon J. Fuller  * @retval true if @p chip matches @p match.
7875ad9ac03SAdrian Chadd  * @retval false if @p chip does not match @p match.
7885ad9ac03SAdrian Chadd  */
7895ad9ac03SAdrian Chadd bool
bhnd_board_matches(const struct bhnd_board_info * board,const struct bhnd_board_match * desc)7905ad9ac03SAdrian Chadd bhnd_board_matches(const struct bhnd_board_info *board,
7915ad9ac03SAdrian Chadd     const struct bhnd_board_match *desc)
7925ad9ac03SAdrian Chadd {
7935ad9ac03SAdrian Chadd 	if (desc->m.match.board_srom_rev &&
794d567592bSAdrian Chadd 	    !bhnd_hwrev_matches(board->board_srom_rev, &desc->board_srom_rev))
795d567592bSAdrian Chadd 		return (false);
796d567592bSAdrian Chadd 
7975ad9ac03SAdrian Chadd 	if (desc->m.match.board_vendor &&
7985ad9ac03SAdrian Chadd 	    board->board_vendor != desc->board_vendor)
799d567592bSAdrian Chadd 		return (false);
800d567592bSAdrian Chadd 
8015ad9ac03SAdrian Chadd 	if (desc->m.match.board_type && board->board_type != desc->board_type)
802d567592bSAdrian Chadd 		return (false);
803d567592bSAdrian Chadd 
804566ca880SLandon J. Fuller 	if (desc->m.match.board_devid &&
805566ca880SLandon J. Fuller 	    board->board_devid != desc->board_devid)
806566ca880SLandon J. Fuller 		return (false);
807566ca880SLandon J. Fuller 
8085ad9ac03SAdrian Chadd 	if (desc->m.match.board_rev &&
809d567592bSAdrian Chadd 	    !bhnd_hwrev_matches(board->board_rev, &desc->board_rev))
810d567592bSAdrian Chadd 		return (false);
811d567592bSAdrian Chadd 
812f74f1a68SAdrian Chadd 	return (true);
813f74f1a68SAdrian Chadd }
814f74f1a68SAdrian Chadd 
815f74f1a68SAdrian Chadd /**
8164ad7e9b0SAdrian Chadd  * Return true if the @p hwrev matches @p desc.
8174ad7e9b0SAdrian Chadd  *
8184ad7e9b0SAdrian Chadd  * @param hwrev A bhnd hardware revision.
8194ad7e9b0SAdrian Chadd  * @param desc A match descriptor to compare against @p core.
8204ad7e9b0SAdrian Chadd  *
82105ed3f90SLandon J. Fuller  * @retval true if @p hwrev matches @p match.
8224ad7e9b0SAdrian Chadd  * @retval false if @p hwrev does not match @p match.
8234ad7e9b0SAdrian Chadd  */
8244ad7e9b0SAdrian Chadd bool
bhnd_hwrev_matches(uint16_t hwrev,const struct bhnd_hwrev_match * desc)8254ad7e9b0SAdrian Chadd bhnd_hwrev_matches(uint16_t hwrev, const struct bhnd_hwrev_match *desc)
8264ad7e9b0SAdrian Chadd {
8274ad7e9b0SAdrian Chadd 	if (desc->start != BHND_HWREV_INVALID &&
8284ad7e9b0SAdrian Chadd 	    desc->start > hwrev)
8294ad7e9b0SAdrian Chadd 		return false;
8304ad7e9b0SAdrian Chadd 
8314ad7e9b0SAdrian Chadd 	if (desc->end != BHND_HWREV_INVALID &&
8324ad7e9b0SAdrian Chadd 	    desc->end < hwrev)
8334ad7e9b0SAdrian Chadd 		return false;
8344ad7e9b0SAdrian Chadd 
8354ad7e9b0SAdrian Chadd 	return true;
8364ad7e9b0SAdrian Chadd }
8374ad7e9b0SAdrian Chadd 
8384ad7e9b0SAdrian Chadd /**
8394ad7e9b0SAdrian Chadd  * Return true if the @p dev matches @p desc.
8404ad7e9b0SAdrian Chadd  *
8414ad7e9b0SAdrian Chadd  * @param dev A bhnd device.
8424ad7e9b0SAdrian Chadd  * @param desc A match descriptor to compare against @p dev.
8434ad7e9b0SAdrian Chadd  *
84405ed3f90SLandon J. Fuller  * @retval true if @p dev matches @p match.
8454ad7e9b0SAdrian Chadd  * @retval false if @p dev does not match @p match.
8464ad7e9b0SAdrian Chadd  */
8474ad7e9b0SAdrian Chadd bool
bhnd_device_matches(device_t dev,const struct bhnd_device_match * desc)8485ad9ac03SAdrian Chadd bhnd_device_matches(device_t dev, const struct bhnd_device_match *desc)
8494ad7e9b0SAdrian Chadd {
8505ad9ac03SAdrian Chadd 	struct bhnd_core_info		 core;
8515ad9ac03SAdrian Chadd 	const struct bhnd_chipid	*chip;
8525ad9ac03SAdrian Chadd 	struct bhnd_board_info		 board;
8535ad9ac03SAdrian Chadd 	device_t			 parent;
8545ad9ac03SAdrian Chadd 	int				 error;
8554ad7e9b0SAdrian Chadd 
8565ad9ac03SAdrian Chadd 	/* Construct individual match descriptors */
8575ad9ac03SAdrian Chadd 	struct bhnd_core_match	m_core	= { _BHND_CORE_MATCH_COPY(desc) };
8585ad9ac03SAdrian Chadd 	struct bhnd_chip_match	m_chip	= { _BHND_CHIP_MATCH_COPY(desc) };
8595ad9ac03SAdrian Chadd 	struct bhnd_board_match	m_board	= { _BHND_BOARD_MATCH_COPY(desc) };
8605ad9ac03SAdrian Chadd 
8615ad9ac03SAdrian Chadd 	/* Fetch and match core info */
8625ad9ac03SAdrian Chadd 	if (m_core.m.match_flags) {
8635ad9ac03SAdrian Chadd 		/* Only applicable to bhnd-attached cores */
8645ad9ac03SAdrian Chadd 		parent = device_get_parent(dev);
86558d83161SJohn Baldwin 		if (device_get_devclass(parent) != devclass_find("bhnd")) {
8665ad9ac03SAdrian Chadd 			device_printf(dev, "attempting to match core "
8675ad9ac03SAdrian Chadd 			    "attributes against non-core device\n");
8685ad9ac03SAdrian Chadd 			return (false);
8695ad9ac03SAdrian Chadd 		}
8705ad9ac03SAdrian Chadd 
8715ad9ac03SAdrian Chadd 		core = bhnd_get_core_info(dev);
8725ad9ac03SAdrian Chadd 		if (!bhnd_core_matches(&core, &m_core))
8735ad9ac03SAdrian Chadd 			return (false);
8745ad9ac03SAdrian Chadd 	}
8755ad9ac03SAdrian Chadd 
8765ad9ac03SAdrian Chadd 	/* Fetch and match chip info */
8775ad9ac03SAdrian Chadd 	if (m_chip.m.match_flags) {
8785ad9ac03SAdrian Chadd 		chip = bhnd_get_chipid(dev);
8795ad9ac03SAdrian Chadd 
8805ad9ac03SAdrian Chadd 		if (!bhnd_chip_matches(chip, &m_chip))
8815ad9ac03SAdrian Chadd 			return (false);
8825ad9ac03SAdrian Chadd 	}
8835ad9ac03SAdrian Chadd 
8845ad9ac03SAdrian Chadd 	/* Fetch and match board info.
8855ad9ac03SAdrian Chadd 	 *
8865ad9ac03SAdrian Chadd 	 * This is not available until  after NVRAM is up; earlier device
8875ad9ac03SAdrian Chadd 	 * matches should not include board requirements */
8885ad9ac03SAdrian Chadd 	if (m_board.m.match_flags) {
8895ad9ac03SAdrian Chadd 		if ((error = bhnd_read_board_info(dev, &board))) {
8905ad9ac03SAdrian Chadd 			device_printf(dev, "failed to read required board info "
8915ad9ac03SAdrian Chadd 			    "during device matching: %d\n", error);
8925ad9ac03SAdrian Chadd 			return (false);
8935ad9ac03SAdrian Chadd 		}
8945ad9ac03SAdrian Chadd 
8955ad9ac03SAdrian Chadd 		if (!bhnd_board_matches(&board, &m_board))
8965ad9ac03SAdrian Chadd 			return (false);
8975ad9ac03SAdrian Chadd 	}
8985ad9ac03SAdrian Chadd 
8995ad9ac03SAdrian Chadd 	/* All matched */
9005ad9ac03SAdrian Chadd 	return (true);
9014ad7e9b0SAdrian Chadd }
9024ad7e9b0SAdrian Chadd 
9034ad7e9b0SAdrian Chadd /**
90436e4410aSAdrian Chadd  * Search @p table for an entry matching @p dev.
90536e4410aSAdrian Chadd  *
90636e4410aSAdrian Chadd  * @param dev A bhnd device to match against @p table.
90736e4410aSAdrian Chadd  * @param table The device table to search.
90836e4410aSAdrian Chadd  * @param entry_size The @p table entry size, in bytes.
90936e4410aSAdrian Chadd  *
91005ed3f90SLandon J. Fuller  * @retval non-NULL the first matching device, if any.
91136e4410aSAdrian Chadd  * @retval NULL if no matching device is found in @p table.
91236e4410aSAdrian Chadd  */
91336e4410aSAdrian Chadd const struct bhnd_device *
bhnd_device_lookup(device_t dev,const struct bhnd_device * table,size_t entry_size)91436e4410aSAdrian Chadd bhnd_device_lookup(device_t dev, const struct bhnd_device *table,
91536e4410aSAdrian Chadd     size_t entry_size)
91636e4410aSAdrian Chadd {
91736e4410aSAdrian Chadd 	const struct bhnd_device	*entry;
918d9352570SAdrian Chadd 	device_t			 hostb, parent;
9195ad9ac03SAdrian Chadd 	bhnd_attach_type		 attach_type;
9205ad9ac03SAdrian Chadd 	uint32_t			 dflags;
921d9352570SAdrian Chadd 
922d9352570SAdrian Chadd 	parent = device_get_parent(dev);
9238e35bf83SLandon J. Fuller 	hostb = bhnd_bus_find_hostb_device(parent);
9245ad9ac03SAdrian Chadd 	attach_type = bhnd_get_attach_type(dev);
92536e4410aSAdrian Chadd 
9265ad9ac03SAdrian Chadd 	for (entry = table; !BHND_DEVICE_IS_END(entry); entry =
92736e4410aSAdrian Chadd 	    (const struct bhnd_device *) ((const char *) entry + entry_size))
92836e4410aSAdrian Chadd 	{
92936e4410aSAdrian Chadd 		/* match core info */
93036e4410aSAdrian Chadd 		if (!bhnd_device_matches(dev, &entry->core))
93136e4410aSAdrian Chadd 			continue;
93236e4410aSAdrian Chadd 
93336e4410aSAdrian Chadd 		/* match device flags */
9345ad9ac03SAdrian Chadd 		dflags = entry->device_flags;
9355ad9ac03SAdrian Chadd 
9365ad9ac03SAdrian Chadd 		/* hostb implies BHND_ATTACH_ADAPTER requirement */
9375ad9ac03SAdrian Chadd 		if (dflags & BHND_DF_HOSTB)
9385ad9ac03SAdrian Chadd 			dflags |= BHND_DF_ADAPTER;
9395ad9ac03SAdrian Chadd 
9405ad9ac03SAdrian Chadd 		if (dflags & BHND_DF_ADAPTER)
9415ad9ac03SAdrian Chadd 			if (attach_type != BHND_ATTACH_ADAPTER)
9425ad9ac03SAdrian Chadd 				continue;
9435ad9ac03SAdrian Chadd 
9445ad9ac03SAdrian Chadd 		if (dflags & BHND_DF_HOSTB)
945d9352570SAdrian Chadd 			if (dev != hostb)
94636e4410aSAdrian Chadd 				continue;
9475ad9ac03SAdrian Chadd 
9485ad9ac03SAdrian Chadd 		if (dflags & BHND_DF_SOC)
9495ad9ac03SAdrian Chadd 			if (attach_type != BHND_ATTACH_NATIVE)
9505ad9ac03SAdrian Chadd 				continue;
95136e4410aSAdrian Chadd 
95236e4410aSAdrian Chadd 		/* device found */
95336e4410aSAdrian Chadd 		return (entry);
95436e4410aSAdrian Chadd 	}
95536e4410aSAdrian Chadd 
95636e4410aSAdrian Chadd 	/* not found */
95736e4410aSAdrian Chadd 	return (NULL);
95836e4410aSAdrian Chadd }
95936e4410aSAdrian Chadd 
96036e4410aSAdrian Chadd /**
9615ad9ac03SAdrian Chadd  * Scan the device @p table for all quirk flags applicable to @p dev.
96236e4410aSAdrian Chadd  *
96336e4410aSAdrian Chadd  * @param dev A bhnd device to match against @p table.
96436e4410aSAdrian Chadd  * @param table The device table to search.
96505ed3f90SLandon J. Fuller  * @param entry_size The @p table entry size, in bytes.
96636e4410aSAdrian Chadd  *
96705ed3f90SLandon J. Fuller  * @return all matching quirk flags.
96836e4410aSAdrian Chadd  */
96936e4410aSAdrian Chadd uint32_t
bhnd_device_quirks(device_t dev,const struct bhnd_device * table,size_t entry_size)97036e4410aSAdrian Chadd bhnd_device_quirks(device_t dev, const struct bhnd_device *table,
97136e4410aSAdrian Chadd     size_t entry_size)
97236e4410aSAdrian Chadd {
97336e4410aSAdrian Chadd 	const struct bhnd_device	*dent;
9745ad9ac03SAdrian Chadd 	const struct bhnd_device_quirk	*qent, *qtable;
97536e4410aSAdrian Chadd 	uint32_t			 quirks;
97636e4410aSAdrian Chadd 
9775ad9ac03SAdrian Chadd 	/* Locate the device entry */
9785ad9ac03SAdrian Chadd 	if ((dent = bhnd_device_lookup(dev, table, entry_size)) == NULL)
97936e4410aSAdrian Chadd 		return (0);
9805ad9ac03SAdrian Chadd 
9815ad9ac03SAdrian Chadd 	/* Quirks table is optional */
9825ad9ac03SAdrian Chadd 	qtable = dent->quirks_table;
9835ad9ac03SAdrian Chadd 	if (qtable == NULL)
9845ad9ac03SAdrian Chadd 		return (0);
98536e4410aSAdrian Chadd 
986d567592bSAdrian Chadd 	/* Collect matching device quirk entries */
9875ad9ac03SAdrian Chadd 	quirks = 0;
98836e4410aSAdrian Chadd 	for (qent = qtable; !BHND_DEVICE_QUIRK_IS_END(qent); qent++) {
9895ad9ac03SAdrian Chadd 		if (bhnd_device_matches(dev, &qent->desc))
99036e4410aSAdrian Chadd 			quirks |= qent->quirks;
99136e4410aSAdrian Chadd 	}
99236e4410aSAdrian Chadd 
99336e4410aSAdrian Chadd 	return (quirks);
99436e4410aSAdrian Chadd }
99536e4410aSAdrian Chadd 
99636e4410aSAdrian Chadd /**
9974ad7e9b0SAdrian Chadd  * Allocate bhnd(4) resources defined in @p rs from a parent bus.
9984ad7e9b0SAdrian Chadd  *
9994ad7e9b0SAdrian Chadd  * @param dev The device requesting ownership of the resources.
10004ad7e9b0SAdrian Chadd  * @param rs A standard bus resource specification. This will be updated
10014ad7e9b0SAdrian Chadd  * with the allocated resource's RIDs.
10024ad7e9b0SAdrian Chadd  * @param res On success, the allocated bhnd resources.
10034ad7e9b0SAdrian Chadd  *
10044ad7e9b0SAdrian Chadd  * @retval 0 success
10054ad7e9b0SAdrian Chadd  * @retval non-zero if allocation of any non-RF_OPTIONAL resource fails,
10064ad7e9b0SAdrian Chadd  * 		    all allocated resources will be released and a regular
10074ad7e9b0SAdrian Chadd  * 		    unix error code will be returned.
10084ad7e9b0SAdrian Chadd  */
10094ad7e9b0SAdrian Chadd int
bhnd_alloc_resources(device_t dev,struct resource_spec * rs,struct bhnd_resource ** res)10104ad7e9b0SAdrian Chadd bhnd_alloc_resources(device_t dev, struct resource_spec *rs,
10114ad7e9b0SAdrian Chadd     struct bhnd_resource **res)
10124ad7e9b0SAdrian Chadd {
10134ad7e9b0SAdrian Chadd 	/* Initialize output array */
10144ad7e9b0SAdrian Chadd 	for (u_int i = 0; rs[i].type != -1; i++)
10154ad7e9b0SAdrian Chadd 		res[i] = NULL;
10164ad7e9b0SAdrian Chadd 
10174ad7e9b0SAdrian Chadd 	for (u_int i = 0; rs[i].type != -1; i++) {
10184ad7e9b0SAdrian Chadd 		res[i] = bhnd_alloc_resource_any(dev, rs[i].type, &rs[i].rid,
10194ad7e9b0SAdrian Chadd 		    rs[i].flags);
10204ad7e9b0SAdrian Chadd 
10214ad7e9b0SAdrian Chadd 		/* Clean up all allocations on failure */
10224ad7e9b0SAdrian Chadd 		if (res[i] == NULL && !(rs[i].flags & RF_OPTIONAL)) {
10234ad7e9b0SAdrian Chadd 			bhnd_release_resources(dev, rs, res);
10244ad7e9b0SAdrian Chadd 			return (ENXIO);
10254ad7e9b0SAdrian Chadd 		}
10264ad7e9b0SAdrian Chadd 	}
10274ad7e9b0SAdrian Chadd 
10284ad7e9b0SAdrian Chadd 	return (0);
102968c6ae00SEd Maste }
10304ad7e9b0SAdrian Chadd 
10314ad7e9b0SAdrian Chadd /**
10324ad7e9b0SAdrian Chadd  * Release bhnd(4) resources defined in @p rs from a parent bus.
10334ad7e9b0SAdrian Chadd  *
10344ad7e9b0SAdrian Chadd  * @param dev The device that owns the resources.
10354ad7e9b0SAdrian Chadd  * @param rs A standard bus resource specification previously initialized
10364ad7e9b0SAdrian Chadd  * by @p bhnd_alloc_resources.
10374ad7e9b0SAdrian Chadd  * @param res The bhnd resources to be released.
10384ad7e9b0SAdrian Chadd  */
10394ad7e9b0SAdrian Chadd void
bhnd_release_resources(device_t dev,const struct resource_spec * rs,struct bhnd_resource ** res)10404ad7e9b0SAdrian Chadd bhnd_release_resources(device_t dev, const struct resource_spec *rs,
10414ad7e9b0SAdrian Chadd     struct bhnd_resource **res)
10424ad7e9b0SAdrian Chadd {
10434ad7e9b0SAdrian Chadd 	for (u_int i = 0; rs[i].type != -1; i++) {
10444ad7e9b0SAdrian Chadd 		if (res[i] == NULL)
10454ad7e9b0SAdrian Chadd 			continue;
10464ad7e9b0SAdrian Chadd 
10474ad7e9b0SAdrian Chadd 		bhnd_release_resource(dev, rs[i].type, rs[i].rid, res[i]);
10484ad7e9b0SAdrian Chadd 		res[i] = NULL;
10494ad7e9b0SAdrian Chadd 	}
10504ad7e9b0SAdrian Chadd }
10514ad7e9b0SAdrian Chadd 
10524ad7e9b0SAdrian Chadd /**
10534e96bf3aSLandon J. Fuller  * Allocate and return a new per-core PMU clock control/status (clkctl)
10544e96bf3aSLandon J. Fuller  * instance for @p dev.
10554e96bf3aSLandon J. Fuller  *
10564e96bf3aSLandon J. Fuller  * @param dev		The bhnd(4) core device mapped by @p r.
10574e96bf3aSLandon J. Fuller  * @param pmu_dev	The bhnd(4) PMU device, implmenting the bhnd_pmu_if
10584e96bf3aSLandon J. Fuller  *			interface. The caller is responsible for ensuring that
10594e96bf3aSLandon J. Fuller  *			this reference remains valid for the lifetime of the
10604e96bf3aSLandon J. Fuller  *			returned clkctl instance.
10614e96bf3aSLandon J. Fuller  * @param r		A resource mapping the core's clock control register
10624e96bf3aSLandon J. Fuller  * 			(see BHND_CLK_CTL_ST). The caller is responsible for
10634e96bf3aSLandon J. Fuller  *			ensuring that this resource remains valid for the
10644e96bf3aSLandon J. Fuller  *			lifetime of the returned clkctl instance.
10654e96bf3aSLandon J. Fuller  * @param offset	The offset to the clock control register within @p r.
10664e96bf3aSLandon J. Fuller  * @param max_latency	The PMU's maximum state transition latency in
10674e96bf3aSLandon J. Fuller  *			microseconds; this upper bound will be used to busy-wait
10684e96bf3aSLandon J. Fuller  *			on PMU state transitions.
10694e96bf3aSLandon J. Fuller  *
10704e96bf3aSLandon J. Fuller  * @retval non-NULL	success
10714e96bf3aSLandon J. Fuller  * @retval NULL		if allocation fails.
10724e96bf3aSLandon J. Fuller  *
10734e96bf3aSLandon J. Fuller  */
10744e96bf3aSLandon J. Fuller struct bhnd_core_clkctl *
bhnd_alloc_core_clkctl(device_t dev,device_t pmu_dev,struct bhnd_resource * r,bus_size_t offset,u_int max_latency)10754e96bf3aSLandon J. Fuller bhnd_alloc_core_clkctl(device_t dev, device_t pmu_dev, struct bhnd_resource *r,
10764e96bf3aSLandon J. Fuller     bus_size_t offset, u_int max_latency)
10774e96bf3aSLandon J. Fuller {
10784e96bf3aSLandon J. Fuller 	struct bhnd_core_clkctl	*clkctl;
10794e96bf3aSLandon J. Fuller 
10804e96bf3aSLandon J. Fuller 	clkctl = malloc(sizeof(*clkctl), M_BHND, M_ZERO | M_NOWAIT);
10814e96bf3aSLandon J. Fuller 	if (clkctl == NULL)
10824e96bf3aSLandon J. Fuller 		return (NULL);
10834e96bf3aSLandon J. Fuller 
10844e96bf3aSLandon J. Fuller 	clkctl->cc_dev = dev;
10854e96bf3aSLandon J. Fuller 	clkctl->cc_pmu_dev = pmu_dev;
10864e96bf3aSLandon J. Fuller 	clkctl->cc_res = r;
10874e96bf3aSLandon J. Fuller 	clkctl->cc_res_offset = offset;
10884e96bf3aSLandon J. Fuller 	clkctl->cc_max_latency = max_latency;
10894e96bf3aSLandon J. Fuller 	clkctl->cc_quirks = bhnd_device_quirks(dev, bhnd_clkctl_devices,
10904e96bf3aSLandon J. Fuller 	    sizeof(bhnd_clkctl_devices[0]));
10914e96bf3aSLandon J. Fuller 
10924e96bf3aSLandon J. Fuller 	BHND_CLKCTL_LOCK_INIT(clkctl);
10934e96bf3aSLandon J. Fuller 
10944e96bf3aSLandon J. Fuller 	return (clkctl);
10954e96bf3aSLandon J. Fuller }
10964e96bf3aSLandon J. Fuller 
10974e96bf3aSLandon J. Fuller /**
10984e96bf3aSLandon J. Fuller  * Free a clkctl instance previously allocated via bhnd_alloc_core_clkctl().
10994e96bf3aSLandon J. Fuller  *
11004e96bf3aSLandon J. Fuller  * @param clkctl	The clkctl instance to be freed.
11014e96bf3aSLandon J. Fuller  */
11024e96bf3aSLandon J. Fuller void
bhnd_free_core_clkctl(struct bhnd_core_clkctl * clkctl)11034e96bf3aSLandon J. Fuller bhnd_free_core_clkctl(struct bhnd_core_clkctl *clkctl)
11044e96bf3aSLandon J. Fuller {
11054e96bf3aSLandon J. Fuller 	BHND_CLKCTL_LOCK_DESTROY(clkctl);
11064e96bf3aSLandon J. Fuller 
11074e96bf3aSLandon J. Fuller 	free(clkctl, M_BHND);
11084e96bf3aSLandon J. Fuller }
11094e96bf3aSLandon J. Fuller 
11104e96bf3aSLandon J. Fuller /**
11114e96bf3aSLandon J. Fuller  * Wait for the per-core clock status to be equal to @p value after
11124e96bf3aSLandon J. Fuller  * applying @p mask, timing out after the maximum transition latency is reached.
11134e96bf3aSLandon J. Fuller  *
11144e96bf3aSLandon J. Fuller  * @param clkctl	Per-core clkctl state to be queryied.
11154e96bf3aSLandon J. Fuller  * @param value		Value to wait for.
11164e96bf3aSLandon J. Fuller  * @param mask		Mask to apply prior to value comparison.
11174e96bf3aSLandon J. Fuller  *
11184e96bf3aSLandon J. Fuller  * @retval 0		success
11194e96bf3aSLandon J. Fuller  * @retval ETIMEDOUT	if the PMU's maximum transition delay is reached before
11204e96bf3aSLandon J. Fuller  *			the clock status matches @p value and @p mask.
11214e96bf3aSLandon J. Fuller  */
11224e96bf3aSLandon J. Fuller int
bhnd_core_clkctl_wait(struct bhnd_core_clkctl * clkctl,uint32_t value,uint32_t mask)11234e96bf3aSLandon J. Fuller bhnd_core_clkctl_wait(struct bhnd_core_clkctl *clkctl, uint32_t value,
11244e96bf3aSLandon J. Fuller     uint32_t mask)
11254e96bf3aSLandon J. Fuller {
11264e96bf3aSLandon J. Fuller 	uint32_t	clkst;
11274e96bf3aSLandon J. Fuller 
11284e96bf3aSLandon J. Fuller 	BHND_CLKCTL_LOCK_ASSERT(clkctl, MA_OWNED);
11294e96bf3aSLandon J. Fuller 
11304e96bf3aSLandon J. Fuller 	/* Bitswapped HTAVAIL/ALPAVAIL work-around */
11314e96bf3aSLandon J. Fuller 	if (clkctl->cc_quirks & BHND_CLKCTL_QUIRK_CCS0) {
11324e96bf3aSLandon J. Fuller 		uint32_t fmask, fval;
11334e96bf3aSLandon J. Fuller 
11344e96bf3aSLandon J. Fuller 		fmask = mask & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
11354e96bf3aSLandon J. Fuller 		fval = value & ~(BHND_CCS_HTAVAIL | BHND_CCS_ALPAVAIL);
11364e96bf3aSLandon J. Fuller 
11374e96bf3aSLandon J. Fuller 		if (mask & BHND_CCS_HTAVAIL)
11384e96bf3aSLandon J. Fuller 			fmask |= BHND_CCS0_HTAVAIL;
11394e96bf3aSLandon J. Fuller 		if (value & BHND_CCS_HTAVAIL)
11404e96bf3aSLandon J. Fuller 			fval |= BHND_CCS0_HTAVAIL;
11414e96bf3aSLandon J. Fuller 
11424e96bf3aSLandon J. Fuller 		if (mask & BHND_CCS_ALPAVAIL)
11434e96bf3aSLandon J. Fuller 			fmask |= BHND_CCS0_ALPAVAIL;
11444e96bf3aSLandon J. Fuller 		if (value & BHND_CCS_ALPAVAIL)
11454e96bf3aSLandon J. Fuller 			fval |= BHND_CCS0_ALPAVAIL;
11464e96bf3aSLandon J. Fuller 
11474e96bf3aSLandon J. Fuller 		mask = fmask;
11484e96bf3aSLandon J. Fuller 		value = fval;
11494e96bf3aSLandon J. Fuller 	}
11504e96bf3aSLandon J. Fuller 
11514e96bf3aSLandon J. Fuller 	for (u_int i = 0; i < clkctl->cc_max_latency; i += 10) {
11524e96bf3aSLandon J. Fuller 		clkst = bhnd_bus_read_4(clkctl->cc_res, clkctl->cc_res_offset);
11534e96bf3aSLandon J. Fuller 		if ((clkst & mask) == (value & mask))
11544e96bf3aSLandon J. Fuller 			return (0);
11554e96bf3aSLandon J. Fuller 
11564e96bf3aSLandon J. Fuller 		DELAY(10);
11574e96bf3aSLandon J. Fuller 	}
11584e96bf3aSLandon J. Fuller 
11594e96bf3aSLandon J. Fuller 	device_printf(clkctl->cc_dev, "clkst wait timeout (value=%#x, "
11604e96bf3aSLandon J. Fuller 	    "mask=%#x)\n", value, mask);
11614e96bf3aSLandon J. Fuller 
11624e96bf3aSLandon J. Fuller 	return (ETIMEDOUT);
11634e96bf3aSLandon J. Fuller }
11644e96bf3aSLandon J. Fuller 
11654e96bf3aSLandon J. Fuller /**
11661728aef2SLandon J. Fuller  * Read an NVRAM variable's NUL-terminated string value.
11671728aef2SLandon J. Fuller  *
11681728aef2SLandon J. Fuller  * @param 	dev	A bhnd bus child device.
11691728aef2SLandon J. Fuller  * @param	name	The NVRAM variable name.
11701728aef2SLandon J. Fuller  * @param[out]	buf	A buffer large enough to hold @p len bytes. On
11711728aef2SLandon J. Fuller  *			success, the NUL-terminated string value will be
11721728aef2SLandon J. Fuller  *			written to this buffer. This argment may be NULL if
11731728aef2SLandon J. Fuller  *			the value is not desired.
11741728aef2SLandon J. Fuller  * @param	len	The maximum capacity of @p buf.
11751728aef2SLandon J. Fuller  * @param[out]	rlen	On success, will be set to the actual size of
11761728aef2SLandon J. Fuller  *			the requested value (including NUL termination). This
11771728aef2SLandon J. Fuller  *			argment may be NULL if the size is not desired.
11781728aef2SLandon J. Fuller  *
11791728aef2SLandon J. Fuller  * @retval 0		success
11801728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
11811728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
11821728aef2SLandon J. Fuller  * @retval ENOMEM	If @p buf is non-NULL and a buffer of @p len is too
11831728aef2SLandon J. Fuller  *			small to hold the requested value.
11841728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a valid
11851728aef2SLandon J. Fuller  *			string representation.
11861728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow @p type.
11871728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
11881728aef2SLandon J. Fuller  *			error code will be returned.
11891728aef2SLandon J. Fuller  */
11901728aef2SLandon J. Fuller int
bhnd_nvram_getvar_str(device_t dev,const char * name,char * buf,size_t len,size_t * rlen)11911728aef2SLandon J. Fuller bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len,
11921728aef2SLandon J. Fuller     size_t *rlen)
11931728aef2SLandon J. Fuller {
11941728aef2SLandon J. Fuller 	size_t	larg;
11951728aef2SLandon J. Fuller 	int	error;
11961728aef2SLandon J. Fuller 
11971728aef2SLandon J. Fuller 	larg = len;
119877cb4d3eSLandon J. Fuller 	error = bhnd_nvram_getvar(dev, name, buf, &larg,
119977cb4d3eSLandon J. Fuller 	    BHND_NVRAM_TYPE_STRING);
12001728aef2SLandon J. Fuller 	if (rlen != NULL)
12011728aef2SLandon J. Fuller 		*rlen = larg;
12021728aef2SLandon J. Fuller 
12031728aef2SLandon J. Fuller 	return (error);
12041728aef2SLandon J. Fuller }
12051728aef2SLandon J. Fuller 
12061728aef2SLandon J. Fuller /**
12071728aef2SLandon J. Fuller  * Read an NVRAM variable's unsigned integer value.
12081728aef2SLandon J. Fuller  *
12091728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
12101728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
12111728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
12121728aef2SLandon J. Fuller  *				to this pointer.
12131728aef2SLandon J. Fuller  * @param		width	The output integer type width (1, 2, or
12141728aef2SLandon J. Fuller  *				4 bytes).
12151728aef2SLandon J. Fuller  *
12161728aef2SLandon J. Fuller  * @retval 0		success
12171728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
12181728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
12191728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
12201728aef2SLandon J. Fuller  *			a valid unsigned integer representation.
12211728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow) an
12221728aef2SLandon J. Fuller  *			unsigned representation of the given @p width.
12231728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
12241728aef2SLandon J. Fuller  *			error code will be returned.
12251728aef2SLandon J. Fuller  */
12261728aef2SLandon J. Fuller int
bhnd_nvram_getvar_uint(device_t dev,const char * name,void * value,int width)12271728aef2SLandon J. Fuller bhnd_nvram_getvar_uint(device_t dev, const char *name, void *value, int width)
12281728aef2SLandon J. Fuller {
12291728aef2SLandon J. Fuller 	bhnd_nvram_type	type;
12301728aef2SLandon J. Fuller 	size_t		len;
12311728aef2SLandon J. Fuller 
12321728aef2SLandon J. Fuller 	switch (width) {
12331728aef2SLandon J. Fuller 	case 1:
12341728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_UINT8;
12351728aef2SLandon J. Fuller 		break;
12361728aef2SLandon J. Fuller 	case 2:
12371728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_UINT16;
12381728aef2SLandon J. Fuller 		break;
12391728aef2SLandon J. Fuller 	case 4:
12401728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_UINT32;
12411728aef2SLandon J. Fuller 		break;
12421728aef2SLandon J. Fuller 	default:
12431728aef2SLandon J. Fuller 		device_printf(dev, "unsupported NVRAM integer width: %d\n",
12441728aef2SLandon J. Fuller 		    width);
12451728aef2SLandon J. Fuller 		return (EINVAL);
12461728aef2SLandon J. Fuller 	}
12471728aef2SLandon J. Fuller 
12481728aef2SLandon J. Fuller 	len = width;
12491728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar(dev, name, value, &len, type));
12501728aef2SLandon J. Fuller }
12511728aef2SLandon J. Fuller 
12521728aef2SLandon J. Fuller /**
12531728aef2SLandon J. Fuller  * Read an NVRAM variable's unsigned 8-bit integer value.
12541728aef2SLandon J. Fuller  *
12551728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
12561728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
12571728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
12581728aef2SLandon J. Fuller  *				to this pointer.
12591728aef2SLandon J. Fuller  *
12601728aef2SLandon J. Fuller  * @retval 0		success
12611728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
12621728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
12631728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
12641728aef2SLandon J. Fuller  *			a valid unsigned integer representation.
12651728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow) uint8_t.
12661728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
12671728aef2SLandon J. Fuller  *			error code will be returned.
12681728aef2SLandon J. Fuller  */
12691728aef2SLandon J. Fuller int
bhnd_nvram_getvar_uint8(device_t dev,const char * name,uint8_t * value)12701728aef2SLandon J. Fuller bhnd_nvram_getvar_uint8(device_t dev, const char *name, uint8_t *value)
12711728aef2SLandon J. Fuller {
12721728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
12731728aef2SLandon J. Fuller }
12741728aef2SLandon J. Fuller 
12751728aef2SLandon J. Fuller /**
12761728aef2SLandon J. Fuller  * Read an NVRAM variable's unsigned 16-bit integer value.
12771728aef2SLandon J. Fuller  *
12781728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
12791728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
12801728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
12811728aef2SLandon J. Fuller  *				to this pointer.
12821728aef2SLandon J. Fuller  *
12831728aef2SLandon J. Fuller  * @retval 0		success
12841728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
12851728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
12861728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
12871728aef2SLandon J. Fuller  *			a valid unsigned integer representation.
12881728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow)
12891728aef2SLandon J. Fuller  *			uint16_t.
12901728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
12911728aef2SLandon J. Fuller  *			error code will be returned.
12921728aef2SLandon J. Fuller  */
12931728aef2SLandon J. Fuller int
bhnd_nvram_getvar_uint16(device_t dev,const char * name,uint16_t * value)12941728aef2SLandon J. Fuller bhnd_nvram_getvar_uint16(device_t dev, const char *name, uint16_t *value)
12951728aef2SLandon J. Fuller {
12961728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
12971728aef2SLandon J. Fuller }
12981728aef2SLandon J. Fuller 
12991728aef2SLandon J. Fuller /**
13001728aef2SLandon J. Fuller  * Read an NVRAM variable's unsigned 32-bit integer value.
13011728aef2SLandon J. Fuller  *
13021728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
13031728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
13041728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
13051728aef2SLandon J. Fuller  *				to this pointer.
13061728aef2SLandon J. Fuller  *
13071728aef2SLandon J. Fuller  * @retval 0		success
13081728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
13091728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
13101728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
13111728aef2SLandon J. Fuller  *			a valid unsigned integer representation.
13121728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow)
13131728aef2SLandon J. Fuller  *			uint32_t.
13141728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
13151728aef2SLandon J. Fuller  *			error code will be returned.
13161728aef2SLandon J. Fuller  */
13171728aef2SLandon J. Fuller int
bhnd_nvram_getvar_uint32(device_t dev,const char * name,uint32_t * value)13181728aef2SLandon J. Fuller bhnd_nvram_getvar_uint32(device_t dev, const char *name, uint32_t *value)
13191728aef2SLandon J. Fuller {
13201728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_uint(dev, name, value, sizeof(*value)));
13211728aef2SLandon J. Fuller }
13221728aef2SLandon J. Fuller 
13231728aef2SLandon J. Fuller /**
13241728aef2SLandon J. Fuller  * Read an NVRAM variable's signed integer value.
13251728aef2SLandon J. Fuller  *
13261728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
13271728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
13281728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
13291728aef2SLandon J. Fuller  *				to this pointer.
13301728aef2SLandon J. Fuller  * @param		width	The output integer type width (1, 2, or
13311728aef2SLandon J. Fuller  *				4 bytes).
13321728aef2SLandon J. Fuller  *
13331728aef2SLandon J. Fuller  * @retval 0		success
13341728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
13351728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
13361728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
13371728aef2SLandon J. Fuller  *			a valid integer representation.
13381728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow) an
13391728aef2SLandon J. Fuller  *			signed representation of the given @p width.
13401728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
13411728aef2SLandon J. Fuller  *			error code will be returned.
13421728aef2SLandon J. Fuller  */
13431728aef2SLandon J. Fuller int
bhnd_nvram_getvar_int(device_t dev,const char * name,void * value,int width)13441728aef2SLandon J. Fuller bhnd_nvram_getvar_int(device_t dev, const char *name, void *value, int width)
13451728aef2SLandon J. Fuller {
13461728aef2SLandon J. Fuller 	bhnd_nvram_type	type;
13471728aef2SLandon J. Fuller 	size_t		len;
13481728aef2SLandon J. Fuller 
13491728aef2SLandon J. Fuller 	switch (width) {
13501728aef2SLandon J. Fuller 	case 1:
13511728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_INT8;
13521728aef2SLandon J. Fuller 		break;
13531728aef2SLandon J. Fuller 	case 2:
13541728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_INT16;
13551728aef2SLandon J. Fuller 		break;
13561728aef2SLandon J. Fuller 	case 4:
13571728aef2SLandon J. Fuller 		type = BHND_NVRAM_TYPE_INT32;
13581728aef2SLandon J. Fuller 		break;
13591728aef2SLandon J. Fuller 	default:
13601728aef2SLandon J. Fuller 		device_printf(dev, "unsupported NVRAM integer width: %d\n",
13611728aef2SLandon J. Fuller 		    width);
13621728aef2SLandon J. Fuller 		return (EINVAL);
13631728aef2SLandon J. Fuller 	}
13641728aef2SLandon J. Fuller 
13651728aef2SLandon J. Fuller 	len = width;
13661728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar(dev, name, value, &len, type));
13671728aef2SLandon J. Fuller }
13681728aef2SLandon J. Fuller 
13691728aef2SLandon J. Fuller /**
13701728aef2SLandon J. Fuller  * Read an NVRAM variable's signed 8-bit integer value.
13711728aef2SLandon J. Fuller  *
13721728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
13731728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
13741728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
13751728aef2SLandon J. Fuller  *				to this pointer.
13761728aef2SLandon J. Fuller  *
13771728aef2SLandon J. Fuller  * @retval 0		success
13781728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
13791728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
13801728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
13811728aef2SLandon J. Fuller  *			a valid integer representation.
13821728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow) int8_t.
13831728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
13841728aef2SLandon J. Fuller  *			error code will be returned.
13851728aef2SLandon J. Fuller  */
13861728aef2SLandon J. Fuller int
bhnd_nvram_getvar_int8(device_t dev,const char * name,int8_t * value)13871728aef2SLandon J. Fuller bhnd_nvram_getvar_int8(device_t dev, const char *name, int8_t *value)
13881728aef2SLandon J. Fuller {
13891728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
13901728aef2SLandon J. Fuller }
13911728aef2SLandon J. Fuller 
13921728aef2SLandon J. Fuller /**
13931728aef2SLandon J. Fuller  * Read an NVRAM variable's signed 16-bit integer value.
13941728aef2SLandon J. Fuller  *
13951728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
13961728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
13971728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
13981728aef2SLandon J. Fuller  *				to this pointer.
13991728aef2SLandon J. Fuller  *
14001728aef2SLandon J. Fuller  * @retval 0		success
14011728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
14021728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
14031728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
14041728aef2SLandon J. Fuller  *			a valid integer representation.
14051728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow)
14061728aef2SLandon J. Fuller  *			int16_t.
14071728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
14081728aef2SLandon J. Fuller  *			error code will be returned.
14091728aef2SLandon J. Fuller  */
14101728aef2SLandon J. Fuller int
bhnd_nvram_getvar_int16(device_t dev,const char * name,int16_t * value)14111728aef2SLandon J. Fuller bhnd_nvram_getvar_int16(device_t dev, const char *name, int16_t *value)
14121728aef2SLandon J. Fuller {
14131728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
14141728aef2SLandon J. Fuller }
14151728aef2SLandon J. Fuller 
14161728aef2SLandon J. Fuller /**
14171728aef2SLandon J. Fuller  * Read an NVRAM variable's signed 32-bit integer value.
14181728aef2SLandon J. Fuller  *
14191728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
14201728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
14211728aef2SLandon J. Fuller  * @param[out]		value	On success, the requested value will be written
14221728aef2SLandon J. Fuller  *				to this pointer.
14231728aef2SLandon J. Fuller  *
14241728aef2SLandon J. Fuller  * @retval 0		success
14251728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
14261728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
14271728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
14281728aef2SLandon J. Fuller  *			a valid integer representation.
14291728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow)
14301728aef2SLandon J. Fuller  *			int32_t.
14311728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
14321728aef2SLandon J. Fuller  *			error code will be returned.
14331728aef2SLandon J. Fuller  */
14341728aef2SLandon J. Fuller int
bhnd_nvram_getvar_int32(device_t dev,const char * name,int32_t * value)14351728aef2SLandon J. Fuller bhnd_nvram_getvar_int32(device_t dev, const char *name, int32_t *value)
14361728aef2SLandon J. Fuller {
14371728aef2SLandon J. Fuller 	return (bhnd_nvram_getvar_int(dev, name, value, sizeof(*value)));
14381728aef2SLandon J. Fuller }
14391728aef2SLandon J. Fuller 
14401728aef2SLandon J. Fuller /**
14411728aef2SLandon J. Fuller  * Read an NVRAM variable's array value.
14421728aef2SLandon J. Fuller  *
14431728aef2SLandon J. Fuller  * @param 		dev	A bhnd bus child device.
14441728aef2SLandon J. Fuller  * @param		name	The NVRAM variable name.
14451728aef2SLandon J. Fuller  * @param[out]		buf	A buffer large enough to hold @p size bytes.
14461728aef2SLandon J. Fuller  *				On success, the requested value will be written
14471728aef2SLandon J. Fuller  *				to this buffer.
14481728aef2SLandon J. Fuller  * @param[in,out]	size	The required number of bytes to write to
14491728aef2SLandon J. Fuller  *				@p buf.
14501728aef2SLandon J. Fuller  * @param		type	The desired array element data representation.
14511728aef2SLandon J. Fuller  *
14521728aef2SLandon J. Fuller  * @retval 0		success
14531728aef2SLandon J. Fuller  * @retval ENOENT	The requested variable was not found.
14541728aef2SLandon J. Fuller  * @retval ENODEV	No valid NVRAM source could be found.
14551728aef2SLandon J. Fuller  * @retval ENXIO	If less than @p size bytes are available.
14561728aef2SLandon J. Fuller  * @retval ENOMEM	If a buffer of @p size is too small to hold the
14571728aef2SLandon J. Fuller  *			requested value.
14581728aef2SLandon J. Fuller  * @retval EFTYPE	If the variable data cannot be coerced to a
14591728aef2SLandon J. Fuller  *			a valid instance of @p type.
14601728aef2SLandon J. Fuller  * @retval ERANGE	If value coercion would overflow (or underflow) a
14611728aef2SLandon J. Fuller  *			representation of @p type.
14621728aef2SLandon J. Fuller  * @retval non-zero	If reading @p name otherwise fails, a regular unix
14631728aef2SLandon J. Fuller  *			error code will be returned.
14641728aef2SLandon J. Fuller  */
14651728aef2SLandon J. Fuller int
bhnd_nvram_getvar_array(device_t dev,const char * name,void * buf,size_t size,bhnd_nvram_type type)14661728aef2SLandon J. Fuller bhnd_nvram_getvar_array(device_t dev, const char *name, void *buf, size_t size,
14671728aef2SLandon J. Fuller     bhnd_nvram_type type)
14681728aef2SLandon J. Fuller {
14691728aef2SLandon J. Fuller 	size_t	nbytes;
14701728aef2SLandon J. Fuller 	int	error;
14711728aef2SLandon J. Fuller 
14721728aef2SLandon J. Fuller 	/* Attempt read */
14731728aef2SLandon J. Fuller 	nbytes = size;
14741728aef2SLandon J. Fuller 	if ((error = bhnd_nvram_getvar(dev, name, buf, &nbytes, type)))
14751728aef2SLandon J. Fuller 		return (error);
14761728aef2SLandon J. Fuller 
14771728aef2SLandon J. Fuller 	/* Verify that the expected number of bytes were fetched */
14781728aef2SLandon J. Fuller 	if (nbytes < size)
14791728aef2SLandon J. Fuller 		return (ENXIO);
14801728aef2SLandon J. Fuller 
14811728aef2SLandon J. Fuller 	return (0);
14821728aef2SLandon J. Fuller }
14831728aef2SLandon J. Fuller 
14841728aef2SLandon J. Fuller /**
14858e35bf83SLandon J. Fuller  * Initialize a service provider registry.
14868e35bf83SLandon J. Fuller  *
14878e35bf83SLandon J. Fuller  * @param bsr		The service registry to initialize.
14888e35bf83SLandon J. Fuller  *
14898e35bf83SLandon J. Fuller  * @retval 0            success
14908e35bf83SLandon J. Fuller  * @retval non-zero     if an error occurs initializing the service registry,
14918e35bf83SLandon J. Fuller  *                      a regular unix error code will be returned.
14928e35bf83SLandon J. Fuller 
14938e35bf83SLandon J. Fuller  */
14948e35bf83SLandon J. Fuller int
bhnd_service_registry_init(struct bhnd_service_registry * bsr)14958e35bf83SLandon J. Fuller bhnd_service_registry_init(struct bhnd_service_registry *bsr)
14968e35bf83SLandon J. Fuller {
14978e35bf83SLandon J. Fuller 	STAILQ_INIT(&bsr->entries);
14988e35bf83SLandon J. Fuller 	mtx_init(&bsr->lock, "bhnd_service_registry lock", NULL, MTX_DEF);
14998e35bf83SLandon J. Fuller 
15008e35bf83SLandon J. Fuller 	return (0);
15018e35bf83SLandon J. Fuller }
15028e35bf83SLandon J. Fuller 
15038e35bf83SLandon J. Fuller /**
15048e35bf83SLandon J. Fuller  * Release all resources held by @p bsr.
15058e35bf83SLandon J. Fuller  *
15068e35bf83SLandon J. Fuller  * @param bsr		A service registry instance previously successfully
15078e35bf83SLandon J. Fuller  *			initialized via bhnd_service_registry_init().
15088e35bf83SLandon J. Fuller  *
15098e35bf83SLandon J. Fuller  * @retval 0		success
15108e35bf83SLandon J. Fuller  * @retval EBUSY	if active references to service providers registered
15118e35bf83SLandon J. Fuller  *			with @p bsr exist.
15128e35bf83SLandon J. Fuller  */
15138e35bf83SLandon J. Fuller int
bhnd_service_registry_fini(struct bhnd_service_registry * bsr)15148e35bf83SLandon J. Fuller bhnd_service_registry_fini(struct bhnd_service_registry *bsr)
15158e35bf83SLandon J. Fuller {
15168e35bf83SLandon J. Fuller 	struct bhnd_service_entry *entry, *enext;
15178e35bf83SLandon J. Fuller 
15188e35bf83SLandon J. Fuller 	/* Remove everthing we can */
15198e35bf83SLandon J. Fuller 	mtx_lock(&bsr->lock);
15208e35bf83SLandon J. Fuller 	STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) {
15218e35bf83SLandon J. Fuller 		if (entry->refs > 0)
15228e35bf83SLandon J. Fuller 			continue;
15238e35bf83SLandon J. Fuller 
15248e35bf83SLandon J. Fuller 		STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link);
15258e35bf83SLandon J. Fuller 		free(entry, M_BHND);
15268e35bf83SLandon J. Fuller 	}
15278e35bf83SLandon J. Fuller 
15288e35bf83SLandon J. Fuller 	if (!STAILQ_EMPTY(&bsr->entries)) {
15298e35bf83SLandon J. Fuller 		mtx_unlock(&bsr->lock);
15308e35bf83SLandon J. Fuller 		return (EBUSY);
15318e35bf83SLandon J. Fuller 	}
15328e35bf83SLandon J. Fuller 	mtx_unlock(&bsr->lock);
15338e35bf83SLandon J. Fuller 
15348e35bf83SLandon J. Fuller 	mtx_destroy(&bsr->lock);
15358e35bf83SLandon J. Fuller 	return (0);
15368e35bf83SLandon J. Fuller }
15378e35bf83SLandon J. Fuller 
15388e35bf83SLandon J. Fuller /**
15398e35bf83SLandon J. Fuller  * Register a @p provider for the given @p service.
15408e35bf83SLandon J. Fuller  *
15418e35bf83SLandon J. Fuller  * @param bsr		Service registry to be modified.
15428e35bf83SLandon J. Fuller  * @param provider	Service provider to register.
15438e35bf83SLandon J. Fuller  * @param service	Service for which @p provider will be registered.
15448e35bf83SLandon J. Fuller  * @param flags		Service provider flags (see BHND_SPF_*).
15458e35bf83SLandon J. Fuller  *
15468e35bf83SLandon J. Fuller  * @retval 0		success
15478e35bf83SLandon J. Fuller  * @retval EEXIST	if an entry for @p service already exists.
15488e35bf83SLandon J. Fuller  * @retval EINVAL	if @p service is BHND_SERVICE_ANY.
15498e35bf83SLandon J. Fuller  * @retval non-zero	if registering @p provider otherwise fails, a regular
15508e35bf83SLandon J. Fuller  *			unix error code will be returned.
15518e35bf83SLandon J. Fuller  */
15528e35bf83SLandon J. Fuller int
bhnd_service_registry_add(struct bhnd_service_registry * bsr,device_t provider,bhnd_service_t service,uint32_t flags)15538e35bf83SLandon J. Fuller bhnd_service_registry_add(struct bhnd_service_registry *bsr, device_t provider,
15548e35bf83SLandon J. Fuller     bhnd_service_t service, uint32_t flags)
15558e35bf83SLandon J. Fuller {
15568e35bf83SLandon J. Fuller 	struct bhnd_service_entry *entry;
15578e35bf83SLandon J. Fuller 
15588e35bf83SLandon J. Fuller 	if (service == BHND_SERVICE_ANY)
15598e35bf83SLandon J. Fuller 		return (EINVAL);
15608e35bf83SLandon J. Fuller 
15618e35bf83SLandon J. Fuller 	mtx_lock(&bsr->lock);
15628e35bf83SLandon J. Fuller 
15638e35bf83SLandon J. Fuller 	/* Is a service provider already registered? */
15648e35bf83SLandon J. Fuller 	STAILQ_FOREACH(entry, &bsr->entries, link) {
15658e35bf83SLandon J. Fuller 		if (entry->service == service) {
15668e35bf83SLandon J. Fuller 			mtx_unlock(&bsr->lock);
15678e35bf83SLandon J. Fuller 			return (EEXIST);
15688e35bf83SLandon J. Fuller 		}
15698e35bf83SLandon J. Fuller 	}
15708e35bf83SLandon J. Fuller 
15718e35bf83SLandon J. Fuller 	/* Initialize and insert our new entry */
15728e35bf83SLandon J. Fuller 	entry = malloc(sizeof(*entry), M_BHND, M_NOWAIT);
15738e35bf83SLandon J. Fuller 	if (entry == NULL) {
15748e35bf83SLandon J. Fuller 		mtx_unlock(&bsr->lock);
15758e35bf83SLandon J. Fuller 		return (ENOMEM);
15768e35bf83SLandon J. Fuller 	}
15778e35bf83SLandon J. Fuller 
15788e35bf83SLandon J. Fuller 	entry->provider = provider;
15798e35bf83SLandon J. Fuller 	entry->service = service;
15808e35bf83SLandon J. Fuller 	entry->flags = flags;
15818e35bf83SLandon J. Fuller 	refcount_init(&entry->refs, 0);
15828e35bf83SLandon J. Fuller 
15838e35bf83SLandon J. Fuller 	STAILQ_INSERT_HEAD(&bsr->entries, entry, link);
15848e35bf83SLandon J. Fuller 
15858e35bf83SLandon J. Fuller 	mtx_unlock(&bsr->lock);
15868e35bf83SLandon J. Fuller 	return (0);
15878e35bf83SLandon J. Fuller }
15888e35bf83SLandon J. Fuller 
15898e35bf83SLandon J. Fuller /**
15908e35bf83SLandon J. Fuller  * Free an unreferenced registry entry.
15918e35bf83SLandon J. Fuller  *
15928e35bf83SLandon J. Fuller  * @param entry	The entry to be deallocated.
15938e35bf83SLandon J. Fuller  */
15948e35bf83SLandon J. Fuller static void
bhnd_service_registry_free_entry(struct bhnd_service_entry * entry)15958e35bf83SLandon J. Fuller bhnd_service_registry_free_entry(struct bhnd_service_entry *entry)
15968e35bf83SLandon J. Fuller {
15978e35bf83SLandon J. Fuller 	KASSERT(entry->refs == 0, ("provider has active references"));
15988e35bf83SLandon J. Fuller 	free(entry, M_BHND);
15998e35bf83SLandon J. Fuller }
16008e35bf83SLandon J. Fuller 
16018e35bf83SLandon J. Fuller /**
16028e35bf83SLandon J. Fuller  * Attempt to remove the @p service provider registration for @p provider.
16038e35bf83SLandon J. Fuller  *
16048e35bf83SLandon J. Fuller  * @param bsr		The service registry to be modified.
16058e35bf83SLandon J. Fuller  * @param provider	The service provider to be deregistered.
16068e35bf83SLandon J. Fuller  * @param service	The service for which @p provider will be deregistered,
16078e35bf83SLandon J. Fuller  *			or BHND_SERVICE_ANY to remove all service
16088e35bf83SLandon J. Fuller  *			registrations for @p provider.
16098e35bf83SLandon J. Fuller  *
16108e35bf83SLandon J. Fuller  * @retval 0		success
161105ed3f90SLandon J. Fuller  * @retval EBUSY	if active references to @p provider exist; see
16128e35bf83SLandon J. Fuller  *			bhnd_service_registry_retain() and
16138e35bf83SLandon J. Fuller  *			bhnd_service_registry_release().
16148e35bf83SLandon J. Fuller  */
16158e35bf83SLandon J. Fuller int
bhnd_service_registry_remove(struct bhnd_service_registry * bsr,device_t provider,bhnd_service_t service)16168e35bf83SLandon J. Fuller bhnd_service_registry_remove(struct bhnd_service_registry *bsr,
16178e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
16188e35bf83SLandon J. Fuller {
16198e35bf83SLandon J. Fuller 	struct bhnd_service_entry *entry, *enext;
16208e35bf83SLandon J. Fuller 
16218e35bf83SLandon J. Fuller 	mtx_lock(&bsr->lock);
16228e35bf83SLandon J. Fuller 
16238e35bf83SLandon J. Fuller #define	BHND_PROV_MATCH(_e)	\
16248e35bf83SLandon J. Fuller 	((_e)->provider == provider &&	\
16258e35bf83SLandon J. Fuller 	 (service == BHND_SERVICE_ANY || (_e)->service == service))
16268e35bf83SLandon J. Fuller 
16278e35bf83SLandon J. Fuller 	/* Validate matching provider entries before making any
16288e35bf83SLandon J. Fuller 	 * modifications */
16298e35bf83SLandon J. Fuller 	STAILQ_FOREACH(entry, &bsr->entries, link) {
16308e35bf83SLandon J. Fuller 		/* Skip non-matching entries */
16318e35bf83SLandon J. Fuller 		if (!BHND_PROV_MATCH(entry))
16328e35bf83SLandon J. Fuller 			continue;
16338e35bf83SLandon J. Fuller 
16348e35bf83SLandon J. Fuller 		/* Entry is in use? */
16358e35bf83SLandon J. Fuller 		if (entry->refs > 0) {
16368e35bf83SLandon J. Fuller 			mtx_unlock(&bsr->lock);
16378e35bf83SLandon J. Fuller 			return (EBUSY);
16388e35bf83SLandon J. Fuller 		}
16398e35bf83SLandon J. Fuller 	}
16408e35bf83SLandon J. Fuller 
16418e35bf83SLandon J. Fuller 	/* We can now safely remove matching entries */
16428e35bf83SLandon J. Fuller 	STAILQ_FOREACH_SAFE(entry, &bsr->entries, link, enext) {
16438e35bf83SLandon J. Fuller 		/* Skip non-matching entries */
16448e35bf83SLandon J. Fuller 		if (!BHND_PROV_MATCH(entry))
16458e35bf83SLandon J. Fuller 			continue;
16468e35bf83SLandon J. Fuller 
16478e35bf83SLandon J. Fuller 		/* Remove from list */
16488e35bf83SLandon J. Fuller 		STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry, link);
16498e35bf83SLandon J. Fuller 
16508e35bf83SLandon J. Fuller 		/* Free provider entry */
16518e35bf83SLandon J. Fuller 		bhnd_service_registry_free_entry(entry);
16528e35bf83SLandon J. Fuller 	}
16538e35bf83SLandon J. Fuller #undef	BHND_PROV_MATCH
16548e35bf83SLandon J. Fuller 
16558e35bf83SLandon J. Fuller 	mtx_unlock(&bsr->lock);
16568e35bf83SLandon J. Fuller 	return (0);
16578e35bf83SLandon J. Fuller }
16588e35bf83SLandon J. Fuller 
16598e35bf83SLandon J. Fuller /**
16608e35bf83SLandon J. Fuller  * Retain and return a reference to a registered @p service provider, if any.
16618e35bf83SLandon J. Fuller  *
16628e35bf83SLandon J. Fuller  * @param bsr		The service registry to be queried.
16638e35bf83SLandon J. Fuller  * @param service	The service for which a provider should be returned.
16648e35bf83SLandon J. Fuller  *
16658e35bf83SLandon J. Fuller  * On success, the caller assumes ownership the returned provider, and
16668e35bf83SLandon J. Fuller  * is responsible for releasing this reference via
16678e35bf83SLandon J. Fuller  * bhnd_service_registry_release().
16688e35bf83SLandon J. Fuller  *
16698e35bf83SLandon J. Fuller  * @retval device_t	success
16708e35bf83SLandon J. Fuller  * @retval NULL		if no provider is registered for @p service.
16718e35bf83SLandon J. Fuller  */
16728e35bf83SLandon J. Fuller device_t
bhnd_service_registry_retain(struct bhnd_service_registry * bsr,bhnd_service_t service)16738e35bf83SLandon J. Fuller bhnd_service_registry_retain(struct bhnd_service_registry *bsr,
16748e35bf83SLandon J. Fuller     bhnd_service_t service)
16758e35bf83SLandon J. Fuller {
16768e35bf83SLandon J. Fuller 	struct bhnd_service_entry *entry;
16778e35bf83SLandon J. Fuller 
16788e35bf83SLandon J. Fuller 	mtx_lock(&bsr->lock);
16798e35bf83SLandon J. Fuller 	STAILQ_FOREACH(entry, &bsr->entries, link) {
16808e35bf83SLandon J. Fuller 		if (entry->service != service)
16818e35bf83SLandon J. Fuller 			continue;
16828e35bf83SLandon J. Fuller 
16838e35bf83SLandon J. Fuller 		/* With a live refcount, entry is gauranteed to remain alive
16848e35bf83SLandon J. Fuller 		 * after we release our lock */
16858e35bf83SLandon J. Fuller 		refcount_acquire(&entry->refs);
16868e35bf83SLandon J. Fuller 
16878e35bf83SLandon J. Fuller 		mtx_unlock(&bsr->lock);
16888e35bf83SLandon J. Fuller 		return (entry->provider);
16898e35bf83SLandon J. Fuller 	}
16908e35bf83SLandon J. Fuller 	mtx_unlock(&bsr->lock);
16918e35bf83SLandon J. Fuller 
16928e35bf83SLandon J. Fuller 	/* Not found */
16938e35bf83SLandon J. Fuller 	return (NULL);
16948e35bf83SLandon J. Fuller }
16958e35bf83SLandon J. Fuller 
16968e35bf83SLandon J. Fuller /**
16978e35bf83SLandon J. Fuller  * Release a reference to a service provider previously returned by
16988e35bf83SLandon J. Fuller  * bhnd_service_registry_retain().
16998e35bf83SLandon J. Fuller  *
17008e35bf83SLandon J. Fuller  * If this is the last reference to an inherited service provider registration
170105ed3f90SLandon J. Fuller  * (see BHND_SPF_INHERITED), the registration will also be removed, and
17028e35bf83SLandon J. Fuller  * true will be returned.
17038e35bf83SLandon J. Fuller  *
17048e35bf83SLandon J. Fuller  * @param bsr		The service registry from which @p provider
17058e35bf83SLandon J. Fuller  *			was returned.
17068e35bf83SLandon J. Fuller  * @param provider	The provider to be released.
17078e35bf83SLandon J. Fuller  * @param service	The service for which @p provider was previously
17088e35bf83SLandon J. Fuller  *			retained.
17098e35bf83SLandon J. Fuller  * @retval true		The inherited service provider registration was removed;
17108e35bf83SLandon J. Fuller  *			the caller should release its own reference to the
17118e35bf83SLandon J. Fuller  *			provider.
17128e35bf83SLandon J. Fuller  * @retval false	The service provider was not inherited, or active
17138e35bf83SLandon J. Fuller  *			references to the provider remain.
171405ed3f90SLandon J. Fuller  *
171505ed3f90SLandon J. Fuller  * @see BHND_SPF_INHERITED
17168e35bf83SLandon J. Fuller  */
17178e35bf83SLandon J. Fuller bool
bhnd_service_registry_release(struct bhnd_service_registry * bsr,device_t provider,bhnd_service_t service)17188e35bf83SLandon J. Fuller bhnd_service_registry_release(struct bhnd_service_registry *bsr,
17198e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
17208e35bf83SLandon J. Fuller {
17218e35bf83SLandon J. Fuller 	struct bhnd_service_entry *entry;
17228e35bf83SLandon J. Fuller 
17238e35bf83SLandon J. Fuller 	/* Exclusive lock, as we need to prevent any new references to the
17248e35bf83SLandon J. Fuller 	 * entry from being taken if it's to be removed */
17258e35bf83SLandon J. Fuller 	mtx_lock(&bsr->lock);
17268e35bf83SLandon J. Fuller 	STAILQ_FOREACH(entry, &bsr->entries, link) {
17278e35bf83SLandon J. Fuller 		bool removed;
17288e35bf83SLandon J. Fuller 
17298e35bf83SLandon J. Fuller 		if (entry->provider != provider)
17308e35bf83SLandon J. Fuller 			continue;
17318e35bf83SLandon J. Fuller 
17328e35bf83SLandon J. Fuller 		if (entry->service != service)
17338e35bf83SLandon J. Fuller 			continue;
17348e35bf83SLandon J. Fuller 
17358e35bf83SLandon J. Fuller 		if (refcount_release(&entry->refs) &&
17368e35bf83SLandon J. Fuller 		    (entry->flags & BHND_SPF_INHERITED))
17378e35bf83SLandon J. Fuller 		{
17388e35bf83SLandon J. Fuller 			/* If an inherited entry is no longer actively
17398e35bf83SLandon J. Fuller 			 * referenced, remove the local registration and inform
17408e35bf83SLandon J. Fuller 			 * the caller. */
17418e35bf83SLandon J. Fuller 			STAILQ_REMOVE(&bsr->entries, entry, bhnd_service_entry,
17428e35bf83SLandon J. Fuller 			    link);
17438e35bf83SLandon J. Fuller 			bhnd_service_registry_free_entry(entry);
17448e35bf83SLandon J. Fuller 			removed = true;
17458e35bf83SLandon J. Fuller 		} else {
17468e35bf83SLandon J. Fuller 			removed = false;
17478e35bf83SLandon J. Fuller 		}
17488e35bf83SLandon J. Fuller 
17498e35bf83SLandon J. Fuller 		mtx_unlock(&bsr->lock);
17508e35bf83SLandon J. Fuller 		return (removed);
17518e35bf83SLandon J. Fuller 	}
17528e35bf83SLandon J. Fuller 
17538e35bf83SLandon J. Fuller 	/* Caller owns a reference, but no such provider is registered? */
17548e35bf83SLandon J. Fuller 	panic("invalid service provider reference");
17558e35bf83SLandon J. Fuller }
17568e35bf83SLandon J. Fuller 
17578e35bf83SLandon J. Fuller /**
175836e4410aSAdrian Chadd  * Using the bhnd(4) bus-level core information and a custom core name,
175936e4410aSAdrian Chadd  * populate @p dev's device description.
17604ad7e9b0SAdrian Chadd  *
17614ad7e9b0SAdrian Chadd  * @param dev A bhnd-bus attached device.
176205ed3f90SLandon J. Fuller  * @param dev_name The core's name (e.g. "SDIO Device Core").
17634ad7e9b0SAdrian Chadd  */
17644ad7e9b0SAdrian Chadd void
bhnd_set_custom_core_desc(device_t dev,const char * dev_name)176536e4410aSAdrian Chadd bhnd_set_custom_core_desc(device_t dev, const char *dev_name)
17664ad7e9b0SAdrian Chadd {
17674ad7e9b0SAdrian Chadd 	const char *vendor_name;
17684ad7e9b0SAdrian Chadd 
17694ad7e9b0SAdrian Chadd 	vendor_name = bhnd_get_vendor_name(dev);
1770*a866a40bSMark Johnston 	device_set_descf(dev, "%s %s, rev %hhu", vendor_name, dev_name,
17714ad7e9b0SAdrian Chadd 	    bhnd_get_hwrev(dev));
17724ad7e9b0SAdrian Chadd }
1773386fb140SAdrian Chadd 
1774386fb140SAdrian Chadd /**
177536e4410aSAdrian Chadd  * Using the bhnd(4) bus-level core information, populate @p dev's device
177636e4410aSAdrian Chadd  * description.
177736e4410aSAdrian Chadd  *
177836e4410aSAdrian Chadd  * @param dev A bhnd-bus attached device.
177936e4410aSAdrian Chadd  */
178036e4410aSAdrian Chadd void
bhnd_set_default_core_desc(device_t dev)178136e4410aSAdrian Chadd bhnd_set_default_core_desc(device_t dev)
178236e4410aSAdrian Chadd {
178336e4410aSAdrian Chadd 	bhnd_set_custom_core_desc(dev, bhnd_get_device_name(dev));
178436e4410aSAdrian Chadd }
178536e4410aSAdrian Chadd 
17869bb95906SLandon J. Fuller /**
17879bb95906SLandon J. Fuller  * Using the bhnd @p chip_id, populate the bhnd(4) bus @p dev's device
17889bb95906SLandon J. Fuller  * description.
17899bb95906SLandon J. Fuller  *
17909bb95906SLandon J. Fuller  * @param dev A bhnd-bus attached device.
179105ed3f90SLandon J. Fuller  * @param chip_id The chip identification.
17929bb95906SLandon J. Fuller  */
17939bb95906SLandon J. Fuller void
bhnd_set_default_bus_desc(device_t dev,const struct bhnd_chipid * chip_id)17949bb95906SLandon J. Fuller bhnd_set_default_bus_desc(device_t dev, const struct bhnd_chipid *chip_id)
17959bb95906SLandon J. Fuller {
17969bb95906SLandon J. Fuller 	const char	*bus_name;
17979bb95906SLandon J. Fuller 	char		 chip_name[BHND_CHIPID_MAX_NAMELEN];
17989bb95906SLandon J. Fuller 
17999bb95906SLandon J. Fuller 	/* Determine chip type's bus name */
18009bb95906SLandon J. Fuller 	switch (chip_id->chip_type) {
18019bb95906SLandon J. Fuller 	case BHND_CHIPTYPE_SIBA:
18029bb95906SLandon J. Fuller 		bus_name = "SIBA bus";
18039bb95906SLandon J. Fuller 		break;
18049bb95906SLandon J. Fuller 	case BHND_CHIPTYPE_BCMA:
18059bb95906SLandon J. Fuller 	case BHND_CHIPTYPE_BCMA_ALT:
18069bb95906SLandon J. Fuller 		bus_name = "BCMA bus";
18079bb95906SLandon J. Fuller 		break;
18089bb95906SLandon J. Fuller 	case BHND_CHIPTYPE_UBUS:
18099bb95906SLandon J. Fuller 		bus_name = "UBUS bus";
18109bb95906SLandon J. Fuller 		break;
18119bb95906SLandon J. Fuller 	default:
18129bb95906SLandon J. Fuller 		bus_name = "Unknown Type";
18139bb95906SLandon J. Fuller 		break;
18149bb95906SLandon J. Fuller 	}
18159bb95906SLandon J. Fuller 
18169bb95906SLandon J. Fuller 	/* Format chip name */
18179bb95906SLandon J. Fuller 	bhnd_format_chip_id(chip_name, sizeof(chip_name),
18189bb95906SLandon J. Fuller 	     chip_id->chip_id);
18199bb95906SLandon J. Fuller 
18209bb95906SLandon J. Fuller 	/* Format and set device description */
1821*a866a40bSMark Johnston 	device_set_descf(dev, "%s %s", chip_name, bus_name);
18229bb95906SLandon J. Fuller }
18239bb95906SLandon J. Fuller 
182436e4410aSAdrian Chadd /**
18258e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_REGISTER_PROVIDER().
18268e35bf83SLandon J. Fuller  *
18278e35bf83SLandon J. Fuller  * This implementation delegates the request to the BHND_BUS_REGISTER_PROVIDER()
18288e35bf83SLandon J. Fuller  * method on the parent of @p dev. If no parent exists, the implementation
18298e35bf83SLandon J. Fuller  * will return an error.
18308e35bf83SLandon J. Fuller  */
18318e35bf83SLandon J. Fuller int
bhnd_bus_generic_register_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)18328e35bf83SLandon J. Fuller bhnd_bus_generic_register_provider(device_t dev, device_t child,
18338e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
18348e35bf83SLandon J. Fuller {
18358e35bf83SLandon J. Fuller 	device_t parent = device_get_parent(dev);
18368e35bf83SLandon J. Fuller 
18378e35bf83SLandon J. Fuller 	if (parent != NULL) {
18388e35bf83SLandon J. Fuller 		return (BHND_BUS_REGISTER_PROVIDER(parent, child,
18398e35bf83SLandon J. Fuller 		    provider, service));
18408e35bf83SLandon J. Fuller 	}
18418e35bf83SLandon J. Fuller 
18428e35bf83SLandon J. Fuller 	return (ENXIO);
18438e35bf83SLandon J. Fuller }
18448e35bf83SLandon J. Fuller 
18458e35bf83SLandon J. Fuller /**
18468e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER().
18478e35bf83SLandon J. Fuller  *
18488e35bf83SLandon J. Fuller  * This implementation delegates the request to the
18498e35bf83SLandon J. Fuller  * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
18508e35bf83SLandon J. Fuller  * exists, the implementation will panic.
18518e35bf83SLandon J. Fuller  */
18528e35bf83SLandon J. Fuller int
bhnd_bus_generic_deregister_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)18538e35bf83SLandon J. Fuller bhnd_bus_generic_deregister_provider(device_t dev, device_t child,
18548e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
18558e35bf83SLandon J. Fuller {
18568e35bf83SLandon J. Fuller 	device_t parent = device_get_parent(dev);
18578e35bf83SLandon J. Fuller 
18588e35bf83SLandon J. Fuller 	if (parent != NULL) {
18598e35bf83SLandon J. Fuller 		return (BHND_BUS_DEREGISTER_PROVIDER(parent, child,
18608e35bf83SLandon J. Fuller 		    provider, service));
18618e35bf83SLandon J. Fuller 	}
18628e35bf83SLandon J. Fuller 
18638e35bf83SLandon J. Fuller 	panic("missing BHND_BUS_DEREGISTER_PROVIDER()");
18648e35bf83SLandon J. Fuller }
18658e35bf83SLandon J. Fuller 
18668e35bf83SLandon J. Fuller /**
18678e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_RETAIN_PROVIDER().
18688e35bf83SLandon J. Fuller  *
18698e35bf83SLandon J. Fuller  * This implementation delegates the request to the
18708e35bf83SLandon J. Fuller  * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
18718e35bf83SLandon J. Fuller  * exists, the implementation will return NULL.
18728e35bf83SLandon J. Fuller  */
18738e35bf83SLandon J. Fuller device_t
bhnd_bus_generic_retain_provider(device_t dev,device_t child,bhnd_service_t service)18748e35bf83SLandon J. Fuller bhnd_bus_generic_retain_provider(device_t dev, device_t child,
18758e35bf83SLandon J. Fuller     bhnd_service_t service)
18768e35bf83SLandon J. Fuller {
18778e35bf83SLandon J. Fuller 	device_t parent = device_get_parent(dev);
18788e35bf83SLandon J. Fuller 
18798e35bf83SLandon J. Fuller 	if (parent != NULL) {
18808e35bf83SLandon J. Fuller 		return (BHND_BUS_RETAIN_PROVIDER(parent, child,
18818e35bf83SLandon J. Fuller 		    service));
18828e35bf83SLandon J. Fuller 	}
18838e35bf83SLandon J. Fuller 
18848e35bf83SLandon J. Fuller 	return (NULL);
18858e35bf83SLandon J. Fuller }
18868e35bf83SLandon J. Fuller 
18878e35bf83SLandon J. Fuller /**
18888e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_RELEASE_PROVIDER().
18898e35bf83SLandon J. Fuller  *
18908e35bf83SLandon J. Fuller  * This implementation delegates the request to the
18918e35bf83SLandon J. Fuller  * BHND_BUS_DEREGISTER_PROVIDER() method on the parent of @p dev. If no parent
18928e35bf83SLandon J. Fuller  * exists, the implementation will panic.
18938e35bf83SLandon J. Fuller  */
18948e35bf83SLandon J. Fuller void
bhnd_bus_generic_release_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)18958e35bf83SLandon J. Fuller bhnd_bus_generic_release_provider(device_t dev, device_t child,
18968e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
18978e35bf83SLandon J. Fuller {
18988e35bf83SLandon J. Fuller 	device_t parent = device_get_parent(dev);
18998e35bf83SLandon J. Fuller 
19008e35bf83SLandon J. Fuller 	if (parent != NULL) {
19018e35bf83SLandon J. Fuller 		return (BHND_BUS_RELEASE_PROVIDER(parent, child,
19028e35bf83SLandon J. Fuller 		    provider, service));
19038e35bf83SLandon J. Fuller 	}
19048e35bf83SLandon J. Fuller 
19058e35bf83SLandon J. Fuller 	panic("missing BHND_BUS_RELEASE_PROVIDER()");
19068e35bf83SLandon J. Fuller }
19078e35bf83SLandon J. Fuller 
19088e35bf83SLandon J. Fuller /**
19098e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_REGISTER_PROVIDER().
19108e35bf83SLandon J. Fuller  *
19118e35bf83SLandon J. Fuller  * This implementation uses the bhnd_service_registry_add() function to
19128e35bf83SLandon J. Fuller  * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
19138e35bf83SLandon J. Fuller  * a suitable service registry to edit.
19148e35bf83SLandon J. Fuller  */
19158e35bf83SLandon J. Fuller int
bhnd_bus_generic_sr_register_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)19168e35bf83SLandon J. Fuller bhnd_bus_generic_sr_register_provider(device_t dev, device_t child,
19178e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
19188e35bf83SLandon J. Fuller {
19198e35bf83SLandon J. Fuller 	struct bhnd_service_registry *bsr;
19208e35bf83SLandon J. Fuller 
19218e35bf83SLandon J. Fuller 	bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
19228e35bf83SLandon J. Fuller 
19238e35bf83SLandon J. Fuller 	KASSERT(bsr != NULL, ("NULL service registry"));
19248e35bf83SLandon J. Fuller 
19258e35bf83SLandon J. Fuller 	return (bhnd_service_registry_add(bsr, provider, service, 0));
19268e35bf83SLandon J. Fuller }
19278e35bf83SLandon J. Fuller 
19288e35bf83SLandon J. Fuller /**
19298e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_DEREGISTER_PROVIDER().
19308e35bf83SLandon J. Fuller  *
19318e35bf83SLandon J. Fuller  * This implementation uses the bhnd_service_registry_remove() function to
19328e35bf83SLandon J. Fuller  * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
19338e35bf83SLandon J. Fuller  * a suitable service registry to edit.
19348e35bf83SLandon J. Fuller  */
19358e35bf83SLandon J. Fuller int
bhnd_bus_generic_sr_deregister_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)19368e35bf83SLandon J. Fuller bhnd_bus_generic_sr_deregister_provider(device_t dev, device_t child,
19378e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
19388e35bf83SLandon J. Fuller {
19398e35bf83SLandon J. Fuller 	struct bhnd_service_registry *bsr;
19408e35bf83SLandon J. Fuller 
19418e35bf83SLandon J. Fuller 	bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
19428e35bf83SLandon J. Fuller 
19438e35bf83SLandon J. Fuller 	KASSERT(bsr != NULL, ("NULL service registry"));
19448e35bf83SLandon J. Fuller 
19458e35bf83SLandon J. Fuller 	return (bhnd_service_registry_remove(bsr, provider, service));
19468e35bf83SLandon J. Fuller }
19478e35bf83SLandon J. Fuller 
19488e35bf83SLandon J. Fuller /**
19498e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_RETAIN_PROVIDER().
19508e35bf83SLandon J. Fuller  *
19518e35bf83SLandon J. Fuller  * This implementation uses the bhnd_service_registry_retain() function to
19528e35bf83SLandon J. Fuller  * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
19538e35bf83SLandon J. Fuller  * a suitable service registry.
19548e35bf83SLandon J. Fuller  *
19558e35bf83SLandon J. Fuller  * If a local provider for the service is not available, and a parent device is
19568e35bf83SLandon J. Fuller  * available, this implementation will attempt to fetch and locally register
19578e35bf83SLandon J. Fuller  * a service provider reference from the parent of @p dev.
19588e35bf83SLandon J. Fuller  */
19598e35bf83SLandon J. Fuller device_t
bhnd_bus_generic_sr_retain_provider(device_t dev,device_t child,bhnd_service_t service)19608e35bf83SLandon J. Fuller bhnd_bus_generic_sr_retain_provider(device_t dev, device_t child,
19618e35bf83SLandon J. Fuller     bhnd_service_t service)
19628e35bf83SLandon J. Fuller {
19638e35bf83SLandon J. Fuller 	struct bhnd_service_registry	*bsr;
19648e35bf83SLandon J. Fuller 	device_t			 parent, provider;
19658e35bf83SLandon J. Fuller 	int				 error;
19668e35bf83SLandon J. Fuller 
19678e35bf83SLandon J. Fuller 	bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
19688e35bf83SLandon J. Fuller 	KASSERT(bsr != NULL, ("NULL service registry"));
19698e35bf83SLandon J. Fuller 
19708e35bf83SLandon J. Fuller 	/*
19718e35bf83SLandon J. Fuller 	 * Attempt to fetch a service provider reference from either the local
19728e35bf83SLandon J. Fuller 	 * service registry, or if not found, from our parent.
19738e35bf83SLandon J. Fuller 	 *
19748e35bf83SLandon J. Fuller 	 * If we fetch a provider from our parent, we register the provider
19758e35bf83SLandon J. Fuller 	 * with the local service registry to prevent conflicting local
19768e35bf83SLandon J. Fuller 	 * registrations from being added.
19778e35bf83SLandon J. Fuller 	 */
19788e35bf83SLandon J. Fuller 	while (1) {
19798e35bf83SLandon J. Fuller 		/* Check the local service registry first */
19808e35bf83SLandon J. Fuller 		provider = bhnd_service_registry_retain(bsr, service);
19818e35bf83SLandon J. Fuller 		if (provider != NULL)
19828e35bf83SLandon J. Fuller 			return (provider);
19838e35bf83SLandon J. Fuller 
19848e35bf83SLandon J. Fuller 		/* Otherwise, try to delegate to our parent (if any) */
19858e35bf83SLandon J. Fuller 		if ((parent = device_get_parent(dev)) == NULL)
19868e35bf83SLandon J. Fuller 			return (NULL);
19878e35bf83SLandon J. Fuller 
19888e35bf83SLandon J. Fuller 		provider = BHND_BUS_RETAIN_PROVIDER(parent, dev, service);
19898e35bf83SLandon J. Fuller 		if (provider == NULL)
19908e35bf83SLandon J. Fuller 			return (NULL);
19918e35bf83SLandon J. Fuller 
19928e35bf83SLandon J. Fuller 		/* Register the inherited service registration with the local
19938e35bf83SLandon J. Fuller 		 * registry */
19948e35bf83SLandon J. Fuller 		error = bhnd_service_registry_add(bsr, provider, service,
19958e35bf83SLandon J. Fuller 		    BHND_SPF_INHERITED);
19968e35bf83SLandon J. Fuller 		if (error) {
19978e35bf83SLandon J. Fuller 			BHND_BUS_RELEASE_PROVIDER(parent, dev, provider,
19988e35bf83SLandon J. Fuller 			    service);
19998e35bf83SLandon J. Fuller 			if (error == EEXIST) {
20008e35bf83SLandon J. Fuller 				/* A valid service provider was registered
20018e35bf83SLandon J. Fuller 				 * concurrently; retry fetching from the local
20028e35bf83SLandon J. Fuller 				 * registry */
20038e35bf83SLandon J. Fuller 				continue;
20048e35bf83SLandon J. Fuller 			}
20058e35bf83SLandon J. Fuller 
20068e35bf83SLandon J. Fuller 			device_printf(dev, "failed to register service "
20078e35bf83SLandon J. Fuller 			    "provider: %d\n", error);
20088e35bf83SLandon J. Fuller 			return (NULL);
20098e35bf83SLandon J. Fuller 		}
20108e35bf83SLandon J. Fuller 	}
20118e35bf83SLandon J. Fuller }
20128e35bf83SLandon J. Fuller 
20138e35bf83SLandon J. Fuller /**
20148e35bf83SLandon J. Fuller  * Helper function for implementing BHND_BUS_RELEASE_PROVIDER().
20158e35bf83SLandon J. Fuller  *
20168e35bf83SLandon J. Fuller  * This implementation uses the bhnd_service_registry_release() function to
20178e35bf83SLandon J. Fuller  * do most of the work. It calls BHND_BUS_GET_SERVICE_REGISTRY() to find
20188e35bf83SLandon J. Fuller  * a suitable service registry.
20198e35bf83SLandon J. Fuller  */
20208e35bf83SLandon J. Fuller void
bhnd_bus_generic_sr_release_provider(device_t dev,device_t child,device_t provider,bhnd_service_t service)20218e35bf83SLandon J. Fuller bhnd_bus_generic_sr_release_provider(device_t dev, device_t child,
20228e35bf83SLandon J. Fuller     device_t provider, bhnd_service_t service)
20238e35bf83SLandon J. Fuller {
20248e35bf83SLandon J. Fuller 	struct bhnd_service_registry	*bsr;
20258e35bf83SLandon J. Fuller 
20268e35bf83SLandon J. Fuller 	bsr = BHND_BUS_GET_SERVICE_REGISTRY(dev, child);
20278e35bf83SLandon J. Fuller 	KASSERT(bsr != NULL, ("NULL service registry"));
20288e35bf83SLandon J. Fuller 
20298e35bf83SLandon J. Fuller 	/* Release the provider reference; if the refcount hits zero on an
20308e35bf83SLandon J. Fuller 	 * inherited reference, true will be returned, and we need to drop
20318e35bf83SLandon J. Fuller 	 * our own bus reference to the provider */
20328e35bf83SLandon J. Fuller 	if (!bhnd_service_registry_release(bsr, provider, service))
20338e35bf83SLandon J. Fuller 		return;
20348e35bf83SLandon J. Fuller 
20358e35bf83SLandon J. Fuller 	/* Drop our reference to the borrowed provider */
20368e35bf83SLandon J. Fuller 	BHND_BUS_RELEASE_PROVIDER(device_get_parent(dev), dev, provider,
20378e35bf83SLandon J. Fuller 	    service);
20388e35bf83SLandon J. Fuller }
20398e35bf83SLandon J. Fuller 
20408e35bf83SLandon J. Fuller /**
2041386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_IS_HW_DISABLED().
2042386fb140SAdrian Chadd  *
2043386fb140SAdrian Chadd  * If a parent device is available, this implementation delegates the
2044386fb140SAdrian Chadd  * request to the BHND_BUS_IS_HW_DISABLED() method on the parent of @p dev.
2045386fb140SAdrian Chadd  *
2046386fb140SAdrian Chadd  * If no parent device is available (i.e. on a the bus root), the hardware
2047386fb140SAdrian Chadd  * is assumed to be usable and false is returned.
2048386fb140SAdrian Chadd  */
2049386fb140SAdrian Chadd bool
bhnd_bus_generic_is_hw_disabled(device_t dev,device_t child)2050386fb140SAdrian Chadd bhnd_bus_generic_is_hw_disabled(device_t dev, device_t child)
2051386fb140SAdrian Chadd {
2052386fb140SAdrian Chadd 	if (device_get_parent(dev) != NULL)
2053386fb140SAdrian Chadd 		return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child));
2054386fb140SAdrian Chadd 
2055386fb140SAdrian Chadd 	return (false);
2056386fb140SAdrian Chadd }
2057386fb140SAdrian Chadd 
2058386fb140SAdrian Chadd /**
2059386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_GET_CHIPID().
2060386fb140SAdrian Chadd  *
2061386fb140SAdrian Chadd  * This implementation delegates the request to the BHND_BUS_GET_CHIPID()
2062386fb140SAdrian Chadd  * method on the parent of @p dev. If no parent exists, the implementation
2063386fb140SAdrian Chadd  * will panic.
2064386fb140SAdrian Chadd  */
2065386fb140SAdrian Chadd const struct bhnd_chipid *
bhnd_bus_generic_get_chipid(device_t dev,device_t child)2066386fb140SAdrian Chadd bhnd_bus_generic_get_chipid(device_t dev, device_t child)
2067386fb140SAdrian Chadd {
2068386fb140SAdrian Chadd 	if (device_get_parent(dev) != NULL)
2069386fb140SAdrian Chadd 		return (BHND_BUS_GET_CHIPID(device_get_parent(dev), child));
2070386fb140SAdrian Chadd 
2071386fb140SAdrian Chadd 	panic("missing BHND_BUS_GET_CHIPID()");
2072386fb140SAdrian Chadd }
2073386fb140SAdrian Chadd 
20749ed45324SLandon J. Fuller /**
20759ed45324SLandon J. Fuller  * Helper function for implementing BHND_BUS_GET_DMA_TRANSLATION().
20769ed45324SLandon J. Fuller  *
20779ed45324SLandon J. Fuller  * If a parent device is available, this implementation delegates the
20789ed45324SLandon J. Fuller  * request to the BHND_BUS_GET_DMA_TRANSLATION() method on the parent of @p dev.
20799ed45324SLandon J. Fuller  *
20809ed45324SLandon J. Fuller  * If no parent device is available, this implementation will panic.
20819ed45324SLandon J. Fuller  */
20829ed45324SLandon J. Fuller int
bhnd_bus_generic_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)20839ed45324SLandon J. Fuller bhnd_bus_generic_get_dma_translation(device_t dev, device_t child, u_int width,
20849ed45324SLandon J. Fuller     uint32_t flags, bus_dma_tag_t *dmat,
20859ed45324SLandon J. Fuller     struct bhnd_dma_translation *translation)
20869ed45324SLandon J. Fuller {
20879ed45324SLandon J. Fuller 	if (device_get_parent(dev) != NULL) {
20889ed45324SLandon J. Fuller 		return (BHND_BUS_GET_DMA_TRANSLATION(device_get_parent(dev),
20899ed45324SLandon J. Fuller 		    child, width, flags, dmat, translation));
20909ed45324SLandon J. Fuller 	}
20919ed45324SLandon J. Fuller 
20929ed45324SLandon J. Fuller 	panic("missing BHND_BUS_GET_DMA_TRANSLATION()");
20939ed45324SLandon J. Fuller }
20949ed45324SLandon J. Fuller 
2095d567592bSAdrian Chadd /* nvram board_info population macros for bhnd_bus_generic_read_board_info() */
2096d567592bSAdrian Chadd #define	BHND_GV(_dest, _name)	\
20971728aef2SLandon J. Fuller 	bhnd_nvram_getvar_uint(child, BHND_NVAR_ ## _name, &_dest,	\
20981728aef2SLandon J. Fuller 	    sizeof(_dest))
2099d567592bSAdrian Chadd 
2100d567592bSAdrian Chadd #define	REQ_BHND_GV(_dest, _name)		do {			\
2101d567592bSAdrian Chadd 	if ((error = BHND_GV(_dest, _name))) {				\
2102d567592bSAdrian Chadd 		device_printf(dev,					\
2103d567592bSAdrian Chadd 		    "error reading " __STRING(_name) ": %d\n", error);	\
2104d567592bSAdrian Chadd 		return (error);						\
2105d567592bSAdrian Chadd 	}								\
2106d567592bSAdrian Chadd } while(0)
2107d567592bSAdrian Chadd 
2108d567592bSAdrian Chadd #define	OPT_BHND_GV(_dest, _name, _default)	do {			\
2109d567592bSAdrian Chadd 	if ((error = BHND_GV(_dest, _name))) {				\
2110d567592bSAdrian Chadd 		if (error != ENOENT) {					\
2111d567592bSAdrian Chadd 			device_printf(dev,				\
2112d567592bSAdrian Chadd 			    "error reading "				\
2113d567592bSAdrian Chadd 			       __STRING(_name) ": %d\n", error);	\
2114d567592bSAdrian Chadd 			return (error);					\
2115d567592bSAdrian Chadd 		}							\
2116d567592bSAdrian Chadd 		_dest = _default;					\
2117d567592bSAdrian Chadd 	}								\
2118d567592bSAdrian Chadd } while(0)
2119d567592bSAdrian Chadd 
2120d567592bSAdrian Chadd /**
2121d567592bSAdrian Chadd  * Helper function for implementing BHND_BUS_READ_BOARDINFO().
2122d567592bSAdrian Chadd  *
2123d567592bSAdrian Chadd  * This implementation populates @p info with information from NVRAM,
2124d567592bSAdrian Chadd  * defaulting board_vendor and board_type fields to 0 if the
2125d567592bSAdrian Chadd  * requested variables cannot be found.
2126d567592bSAdrian Chadd  *
2127d567592bSAdrian Chadd  * This behavior is correct for most SoCs, but must be overridden on
2128d567592bSAdrian Chadd  * bridged (PCI, PCMCIA, etc) devices to produce a complete bhnd_board_info
2129d567592bSAdrian Chadd  * result.
2130d567592bSAdrian Chadd  */
2131d567592bSAdrian Chadd int
bhnd_bus_generic_read_board_info(device_t dev,device_t child,struct bhnd_board_info * info)2132d567592bSAdrian Chadd bhnd_bus_generic_read_board_info(device_t dev, device_t child,
2133d567592bSAdrian Chadd     struct bhnd_board_info *info)
2134d567592bSAdrian Chadd {
2135d567592bSAdrian Chadd 	int	error;
2136d567592bSAdrian Chadd 
2137d567592bSAdrian Chadd 	OPT_BHND_GV(info->board_vendor,	BOARDVENDOR,	0);
2138d567592bSAdrian Chadd 	OPT_BHND_GV(info->board_type,	BOARDTYPE,	0);	/* srom >= 2 */
2139566ca880SLandon J. Fuller 	OPT_BHND_GV(info->board_devid,	DEVID,		0);	/* srom >= 8 */
2140d567592bSAdrian Chadd 	REQ_BHND_GV(info->board_rev,	BOARDREV);
2141f90f4b65SLandon J. Fuller 	OPT_BHND_GV(info->board_srom_rev,SROMREV,	0);	/* missing in
2142f90f4b65SLandon J. Fuller 								   some SoC
2143f90f4b65SLandon J. Fuller 								   NVRAM */
2144d567592bSAdrian Chadd 	REQ_BHND_GV(info->board_flags,	BOARDFLAGS);
2145d567592bSAdrian Chadd 	OPT_BHND_GV(info->board_flags2,	BOARDFLAGS2,	0);	/* srom >= 4 */
2146d567592bSAdrian Chadd 	OPT_BHND_GV(info->board_flags3,	BOARDFLAGS3,	0);	/* srom >= 11 */
2147d567592bSAdrian Chadd 
2148d567592bSAdrian Chadd 	return (0);
2149d567592bSAdrian Chadd }
2150d567592bSAdrian Chadd 
2151d567592bSAdrian Chadd #undef	BHND_GV
2152d567592bSAdrian Chadd #undef	BHND_GV_REQ
2153d567592bSAdrian Chadd #undef	BHND_GV_OPT
2154d567592bSAdrian Chadd 
2155d567592bSAdrian Chadd /**
2156d567592bSAdrian Chadd  * Helper function for implementing BHND_BUS_GET_NVRAM_VAR().
2157d567592bSAdrian Chadd  *
2158fdedcd9fSLandon J. Fuller  * This implementation searches @p dev for a usable NVRAM child device.
2159d567592bSAdrian Chadd  *
2160d567592bSAdrian Chadd  * If no usable child device is found on @p dev, the request is delegated to
2161d567592bSAdrian Chadd  * the BHND_BUS_GET_NVRAM_VAR() method on the parent of @p dev.
2162d567592bSAdrian Chadd  */
2163d567592bSAdrian Chadd int
bhnd_bus_generic_get_nvram_var(device_t dev,device_t child,const char * name,void * buf,size_t * size,bhnd_nvram_type type)2164d567592bSAdrian Chadd bhnd_bus_generic_get_nvram_var(device_t dev, device_t child, const char *name,
21651728aef2SLandon J. Fuller     void *buf, size_t *size, bhnd_nvram_type type)
2166d567592bSAdrian Chadd {
2167d567592bSAdrian Chadd 	device_t	nvram;
2168d567592bSAdrian Chadd 	device_t	parent;
2169d567592bSAdrian Chadd 
2170d14bc723SWarner Losh 	bus_topo_assert();
2171fdedcd9fSLandon J. Fuller 
2172fdedcd9fSLandon J. Fuller 	/* Look for a directly-attached NVRAM child */
2173fdedcd9fSLandon J. Fuller 	if ((nvram = device_find_child(dev, "bhnd_nvram", -1)) != NULL)
21741728aef2SLandon J. Fuller 		return BHND_NVRAM_GETVAR(nvram, name, buf, size, type);
2175d567592bSAdrian Chadd 
2176d567592bSAdrian Chadd 	/* Try to delegate to parent */
2177d567592bSAdrian Chadd 	if ((parent = device_get_parent(dev)) == NULL)
2178d567592bSAdrian Chadd 		return (ENODEV);
2179d567592bSAdrian Chadd 
2180d567592bSAdrian Chadd 	return (BHND_BUS_GET_NVRAM_VAR(device_get_parent(dev), child,
21811728aef2SLandon J. Fuller 	    name, buf, size, type));
2182d567592bSAdrian Chadd }
2183d567592bSAdrian Chadd 
2184386fb140SAdrian Chadd /**
2185386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_ALLOC_RESOURCE().
2186386fb140SAdrian Chadd  *
2187386fb140SAdrian Chadd  * This implementation of BHND_BUS_ALLOC_RESOURCE() delegates allocation
2188386fb140SAdrian Chadd  * of the underlying resource to BUS_ALLOC_RESOURCE(), and activation
2189386fb140SAdrian Chadd  * to @p dev's BHND_BUS_ACTIVATE_RESOURCE().
2190386fb140SAdrian Chadd  */
2191386fb140SAdrian Chadd struct bhnd_resource *
bhnd_bus_generic_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)2192386fb140SAdrian Chadd bhnd_bus_generic_alloc_resource(device_t dev, device_t child, int type,
2193386fb140SAdrian Chadd 	int *rid, rman_res_t start, rman_res_t end, rman_res_t count,
2194386fb140SAdrian Chadd 	u_int flags)
2195386fb140SAdrian Chadd {
2196386fb140SAdrian Chadd 	struct bhnd_resource	*br;
2197386fb140SAdrian Chadd 	struct resource		*res;
2198386fb140SAdrian Chadd 	int			 error;
2199386fb140SAdrian Chadd 
2200386fb140SAdrian Chadd 	br = NULL;
2201386fb140SAdrian Chadd 	res = NULL;
2202386fb140SAdrian Chadd 
2203386fb140SAdrian Chadd 	/* Allocate the real bus resource (without activating it) */
2204386fb140SAdrian Chadd 	res = BUS_ALLOC_RESOURCE(dev, child, type, rid, start, end, count,
2205386fb140SAdrian Chadd 	    (flags & ~RF_ACTIVE));
2206386fb140SAdrian Chadd 	if (res == NULL)
2207386fb140SAdrian Chadd 		return (NULL);
2208386fb140SAdrian Chadd 
2209386fb140SAdrian Chadd 	/* Allocate our bhnd resource wrapper. */
2210386fb140SAdrian Chadd 	br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT);
2211386fb140SAdrian Chadd 	if (br == NULL)
2212386fb140SAdrian Chadd 		goto failed;
2213386fb140SAdrian Chadd 
2214386fb140SAdrian Chadd 	br->direct = false;
2215386fb140SAdrian Chadd 	br->res = res;
2216386fb140SAdrian Chadd 
2217386fb140SAdrian Chadd 	/* Attempt activation */
2218386fb140SAdrian Chadd 	if (flags & RF_ACTIVE) {
2219386fb140SAdrian Chadd 		error = BHND_BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, br);
2220386fb140SAdrian Chadd 		if (error)
2221386fb140SAdrian Chadd 			goto failed;
2222386fb140SAdrian Chadd 	}
2223386fb140SAdrian Chadd 
2224386fb140SAdrian Chadd 	return (br);
2225386fb140SAdrian Chadd 
2226386fb140SAdrian Chadd failed:
2227386fb140SAdrian Chadd 	if (res != NULL)
22289dbf5b0eSJohn Baldwin 		BUS_RELEASE_RESOURCE(dev, child, res);
2229386fb140SAdrian Chadd 
2230386fb140SAdrian Chadd 	free(br, M_BHND);
2231386fb140SAdrian Chadd 	return (NULL);
2232386fb140SAdrian Chadd }
2233386fb140SAdrian Chadd 
2234386fb140SAdrian Chadd /**
2235386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_RELEASE_RESOURCE().
2236386fb140SAdrian Chadd  *
2237386fb140SAdrian Chadd  * This implementation of BHND_BUS_RELEASE_RESOURCE() delegates release of
2238386fb140SAdrian Chadd  * the backing resource to BUS_RELEASE_RESOURCE().
2239386fb140SAdrian Chadd  */
2240386fb140SAdrian Chadd int
bhnd_bus_generic_release_resource(device_t dev,device_t child,int type,int rid,struct bhnd_resource * r)2241386fb140SAdrian Chadd bhnd_bus_generic_release_resource(device_t dev, device_t child, int type,
2242386fb140SAdrian Chadd     int rid, struct bhnd_resource *r)
2243386fb140SAdrian Chadd {
2244386fb140SAdrian Chadd 	int error;
2245386fb140SAdrian Chadd 
22469dbf5b0eSJohn Baldwin 	if ((error = BUS_RELEASE_RESOURCE(dev, child, r->res)))
2247386fb140SAdrian Chadd 		return (error);
2248386fb140SAdrian Chadd 
2249386fb140SAdrian Chadd 	free(r, M_BHND);
2250386fb140SAdrian Chadd 	return (0);
2251386fb140SAdrian Chadd }
2252386fb140SAdrian Chadd 
2253386fb140SAdrian Chadd /**
2254386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_ACTIVATE_RESOURCE().
2255386fb140SAdrian Chadd  *
2256664a7497SLandon J. Fuller  * This implementation of BHND_BUS_ACTIVATE_RESOURCE() first calls the
2257386fb140SAdrian Chadd  * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev.
2258664a7497SLandon J. Fuller  *
2259664a7497SLandon J. Fuller  * If this fails, and if @p dev is the direct parent of @p child, standard
2260664a7497SLandon J. Fuller  * resource activation is attempted via bus_activate_resource(). This enables
2261664a7497SLandon J. Fuller  * direct use of the bhnd(4) resource APIs on devices that may not be attached
2262664a7497SLandon J. Fuller  * to a parent bhnd bus or bridge.
2263386fb140SAdrian Chadd  */
2264386fb140SAdrian Chadd int
bhnd_bus_generic_activate_resource(device_t dev,device_t child,int type,int rid,struct bhnd_resource * r)2265386fb140SAdrian Chadd bhnd_bus_generic_activate_resource(device_t dev, device_t child, int type,
2266386fb140SAdrian Chadd     int rid, struct bhnd_resource *r)
2267386fb140SAdrian Chadd {
2268664a7497SLandon J. Fuller 	int	error;
2269664a7497SLandon J. Fuller 	bool	passthrough;
2270386fb140SAdrian Chadd 
2271664a7497SLandon J. Fuller 	passthrough = (device_get_parent(child) != dev);
2272664a7497SLandon J. Fuller 
2273664a7497SLandon J. Fuller 	/* Try to delegate to the parent */
2274664a7497SLandon J. Fuller 	if (device_get_parent(dev) != NULL) {
2275664a7497SLandon J. Fuller 		error = BHND_BUS_ACTIVATE_RESOURCE(device_get_parent(dev),
2276664a7497SLandon J. Fuller 		    child, type, rid, r);
2277664a7497SLandon J. Fuller 	} else {
2278664a7497SLandon J. Fuller 		error = ENODEV;
2279664a7497SLandon J. Fuller 	}
2280664a7497SLandon J. Fuller 
2281664a7497SLandon J. Fuller 	/* If bhnd(4) activation has failed and we're the child's direct
2282664a7497SLandon J. Fuller 	 * parent, try falling back on standard resource activation.
2283664a7497SLandon J. Fuller 	 */
2284664a7497SLandon J. Fuller 	if (error && !passthrough) {
2285664a7497SLandon J. Fuller 		error = bus_activate_resource(child, type, rid, r->res);
2286664a7497SLandon J. Fuller 		if (!error)
2287664a7497SLandon J. Fuller 			r->direct = true;
2288664a7497SLandon J. Fuller 	}
2289664a7497SLandon J. Fuller 
2290664a7497SLandon J. Fuller 	return (error);
229168c6ae00SEd Maste }
2292386fb140SAdrian Chadd 
2293386fb140SAdrian Chadd /**
2294386fb140SAdrian Chadd  * Helper function for implementing BHND_BUS_DEACTIVATE_RESOURCE().
2295386fb140SAdrian Chadd  *
2296386fb140SAdrian Chadd  * This implementation of BHND_BUS_ACTIVATE_RESOURCE() simply calls the
2297386fb140SAdrian Chadd  * BHND_BUS_ACTIVATE_RESOURCE() method of the parent of @p dev.
2298386fb140SAdrian Chadd  */
2299386fb140SAdrian Chadd int
bhnd_bus_generic_deactivate_resource(device_t dev,device_t child,int type,int rid,struct bhnd_resource * r)2300386fb140SAdrian Chadd bhnd_bus_generic_deactivate_resource(device_t dev, device_t child,
2301386fb140SAdrian Chadd     int type, int rid, struct bhnd_resource *r)
2302386fb140SAdrian Chadd {
2303386fb140SAdrian Chadd 	if (device_get_parent(dev) != NULL)
2304386fb140SAdrian Chadd 		return (BHND_BUS_DEACTIVATE_RESOURCE(device_get_parent(dev),
2305386fb140SAdrian Chadd 		    child, type, rid, r));
2306386fb140SAdrian Chadd 
2307386fb140SAdrian Chadd 	return (EINVAL);
230868c6ae00SEd Maste }
23092b693a88SLandon J. Fuller 
2310caeff9a3SLandon J. Fuller /**
2311caeff9a3SLandon J. Fuller  * Helper function for implementing BHND_BUS_GET_INTR_DOMAIN().
2312caeff9a3SLandon J. Fuller  *
2313caeff9a3SLandon J. Fuller  * This implementation simply returns the address of nearest bhnd(4) bus,
2314caeff9a3SLandon J. Fuller  * which may be @p dev; this behavior may be incompatible with FDT/OFW targets.
2315caeff9a3SLandon J. Fuller  */
2316caeff9a3SLandon J. Fuller uintptr_t
bhnd_bus_generic_get_intr_domain(device_t dev,device_t child,bool self)2317caeff9a3SLandon J. Fuller bhnd_bus_generic_get_intr_domain(device_t dev, device_t child, bool self)
2318caeff9a3SLandon J. Fuller {
2319caeff9a3SLandon J. Fuller 	return ((uintptr_t)dev);
2320caeff9a3SLandon J. Fuller }
2321