xref: /freebsd-src/sys/dev/bhnd/bcma/bcma.c (revision 20f932af2973afc7624ac40a843b85b48a3947c7)
14ad7e9b0SAdrian Chadd /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
36e778a7eSPedro F. Giffuni  *
4caeff9a3SLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5caeff9a3SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
64ad7e9b0SAdrian Chadd  * All rights reserved.
74ad7e9b0SAdrian Chadd  *
8caeff9a3SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
9caeff9a3SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
10caeff9a3SLandon 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 
364ad7e9b0SAdrian Chadd #include <sys/param.h>
374ad7e9b0SAdrian Chadd #include <sys/bus.h>
384ad7e9b0SAdrian Chadd #include <sys/kernel.h>
394ad7e9b0SAdrian Chadd #include <sys/malloc.h>
404ad7e9b0SAdrian Chadd #include <sys/module.h>
414ad7e9b0SAdrian Chadd #include <sys/systm.h>
424ad7e9b0SAdrian Chadd 
434ad7e9b0SAdrian Chadd #include <machine/bus.h>
444ad7e9b0SAdrian Chadd 
458a03f98aSLandon J. Fuller #include <dev/bhnd/cores/pmu/bhnd_pmu.h>
464ad7e9b0SAdrian Chadd 
47824b48efSLandon J. Fuller #include "bcma_dmp.h"
48824b48efSLandon J. Fuller 
494ad7e9b0SAdrian Chadd #include "bcma_eromreg.h"
504ad7e9b0SAdrian Chadd #include "bcma_eromvar.h"
51824b48efSLandon J. Fuller 
528a03f98aSLandon J. Fuller #include "bcmavar.h"
534ad7e9b0SAdrian Chadd 
54664a7497SLandon J. Fuller /* RID used when allocating EROM table */
55664a7497SLandon J. Fuller #define	BCMA_EROM_RID	0
56664a7497SLandon J. Fuller 
57111d7cb2SLandon J. Fuller static bhnd_erom_class_t *
58111d7cb2SLandon J. Fuller bcma_get_erom_class(driver_t *driver)
59111d7cb2SLandon J. Fuller {
60111d7cb2SLandon J. Fuller 	return (&bcma_erom_parser);
61111d7cb2SLandon J. Fuller }
62111d7cb2SLandon J. Fuller 
634ad7e9b0SAdrian Chadd int
644ad7e9b0SAdrian Chadd bcma_probe(device_t dev)
654ad7e9b0SAdrian Chadd {
664ad7e9b0SAdrian Chadd 	device_set_desc(dev, "BCMA BHND bus");
674ad7e9b0SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
684ad7e9b0SAdrian Chadd }
694ad7e9b0SAdrian Chadd 
70111d7cb2SLandon J. Fuller /**
71111d7cb2SLandon J. Fuller  * Default bcma(4) bus driver implementation of DEVICE_ATTACH().
72111d7cb2SLandon J. Fuller  *
73111d7cb2SLandon J. Fuller  * This implementation initializes internal bcma(4) state and performs
74111d7cb2SLandon J. Fuller  * bus enumeration, and must be called by subclassing drivers in
75111d7cb2SLandon J. Fuller  * DEVICE_ATTACH() before any other bus methods.
76111d7cb2SLandon J. Fuller  */
774ad7e9b0SAdrian Chadd int
784ad7e9b0SAdrian Chadd bcma_attach(device_t dev)
794ad7e9b0SAdrian Chadd {
804ad7e9b0SAdrian Chadd 	int error;
814ad7e9b0SAdrian Chadd 
82111d7cb2SLandon J. Fuller 	/* Enumerate children */
83111d7cb2SLandon J. Fuller 	if ((error = bcma_add_children(dev))) {
844ad7e9b0SAdrian Chadd 		return (error);
854ad7e9b0SAdrian Chadd 	}
864ad7e9b0SAdrian Chadd 
87111d7cb2SLandon J. Fuller 	return (0);
884ad7e9b0SAdrian Chadd }
894ad7e9b0SAdrian Chadd 
904ad7e9b0SAdrian Chadd int
914ad7e9b0SAdrian Chadd bcma_detach(device_t dev)
924ad7e9b0SAdrian Chadd {
934ad7e9b0SAdrian Chadd 	return (bhnd_generic_detach(dev));
944ad7e9b0SAdrian Chadd }
954ad7e9b0SAdrian Chadd 
968a03f98aSLandon J. Fuller static device_t
978a03f98aSLandon J. Fuller bcma_add_child(device_t dev, u_int order, const char *name, int unit)
988a03f98aSLandon J. Fuller {
998a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
1008a03f98aSLandon J. Fuller 	device_t		 child;
1018a03f98aSLandon J. Fuller 
1028a03f98aSLandon J. Fuller 	child = device_add_child_ordered(dev, order, name, unit);
1038a03f98aSLandon J. Fuller 	if (child == NULL)
1048a03f98aSLandon J. Fuller 		return (NULL);
1058a03f98aSLandon J. Fuller 
1068a03f98aSLandon J. Fuller 	if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
1078a03f98aSLandon J. Fuller 		device_delete_child(dev, child);
1088a03f98aSLandon J. Fuller 		return (NULL);
1098a03f98aSLandon J. Fuller 	}
1108a03f98aSLandon J. Fuller 
1118a03f98aSLandon J. Fuller 	device_set_ivars(child, dinfo);
1128a03f98aSLandon J. Fuller 
1138a03f98aSLandon J. Fuller 	return (child);
1148a03f98aSLandon J. Fuller }
1158a03f98aSLandon J. Fuller 
1168a03f98aSLandon J. Fuller static void
1178a03f98aSLandon J. Fuller bcma_child_deleted(device_t dev, device_t child)
1188a03f98aSLandon J. Fuller {
1198a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
1208a03f98aSLandon J. Fuller 
1218a03f98aSLandon J. Fuller 	/* Call required bhnd(4) implementation */
1228a03f98aSLandon J. Fuller 	bhnd_generic_child_deleted(dev, child);
1238a03f98aSLandon J. Fuller 
1248a03f98aSLandon J. Fuller 	/* Free bcma device info */
1258a03f98aSLandon J. Fuller 	if ((dinfo = device_get_ivars(child)) != NULL)
126caeff9a3SLandon J. Fuller 		bcma_free_dinfo(dev, child, dinfo);
1278a03f98aSLandon J. Fuller 
1288a03f98aSLandon J. Fuller 	device_set_ivars(child, NULL);
1298a03f98aSLandon J. Fuller }
1308a03f98aSLandon J. Fuller 
1314ad7e9b0SAdrian Chadd static int
1324ad7e9b0SAdrian Chadd bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
1334ad7e9b0SAdrian Chadd {
1344ad7e9b0SAdrian Chadd 	const struct bcma_devinfo *dinfo;
1354ad7e9b0SAdrian Chadd 	const struct bhnd_core_info *ci;
1364ad7e9b0SAdrian Chadd 
1374ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
1384ad7e9b0SAdrian Chadd 	ci = &dinfo->corecfg->core_info;
1394ad7e9b0SAdrian Chadd 
1404ad7e9b0SAdrian Chadd 	switch (index) {
1414ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1424ad7e9b0SAdrian Chadd 		*result = ci->vendor;
1434ad7e9b0SAdrian Chadd 		return (0);
1444ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1454ad7e9b0SAdrian Chadd 		*result = ci->device;
1464ad7e9b0SAdrian Chadd 		return (0);
1474ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1484ad7e9b0SAdrian Chadd 		*result = ci->hwrev;
1494ad7e9b0SAdrian Chadd 		return (0);
1504ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1514ad7e9b0SAdrian Chadd 		*result = bhnd_core_class(ci);
1524ad7e9b0SAdrian Chadd 		return (0);
1534ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1544ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
1554ad7e9b0SAdrian Chadd 		return (0);
1564ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1574ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_core_name(ci);
1584ad7e9b0SAdrian Chadd 		return (0);
1594ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1604ad7e9b0SAdrian Chadd 		*result = ci->core_idx;
1614ad7e9b0SAdrian Chadd 		return (0);
1624ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1634ad7e9b0SAdrian Chadd 		*result = ci->unit;
1644ad7e9b0SAdrian Chadd 		return (0);
1658a03f98aSLandon J. Fuller 	case BHND_IVAR_PMU_INFO:
1668a03f98aSLandon J. Fuller 		*result = (uintptr_t) dinfo->pmu_info;
1678a03f98aSLandon J. Fuller 		return (0);
1684ad7e9b0SAdrian Chadd 	default:
1694ad7e9b0SAdrian Chadd 		return (ENOENT);
1704ad7e9b0SAdrian Chadd 	}
1714ad7e9b0SAdrian Chadd }
1724ad7e9b0SAdrian Chadd 
1734ad7e9b0SAdrian Chadd static int
1744ad7e9b0SAdrian Chadd bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
1754ad7e9b0SAdrian Chadd {
1768a03f98aSLandon J. Fuller 	struct bcma_devinfo *dinfo;
1778a03f98aSLandon J. Fuller 
1788a03f98aSLandon J. Fuller 	dinfo = device_get_ivars(child);
1798a03f98aSLandon J. Fuller 
1804ad7e9b0SAdrian Chadd 	switch (index) {
1814ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1824ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1834ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1844ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1854ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1864ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1874ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1884ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1894ad7e9b0SAdrian Chadd 		return (EINVAL);
1908a03f98aSLandon J. Fuller 	case BHND_IVAR_PMU_INFO:
1914e96bf3aSLandon J. Fuller 		dinfo->pmu_info = (void *)value;
1928a03f98aSLandon J. Fuller 		return (0);
1934ad7e9b0SAdrian Chadd 	default:
1944ad7e9b0SAdrian Chadd 		return (ENOENT);
1954ad7e9b0SAdrian Chadd 	}
1964ad7e9b0SAdrian Chadd }
1974ad7e9b0SAdrian Chadd 
1984ad7e9b0SAdrian Chadd static struct resource_list *
1994ad7e9b0SAdrian Chadd bcma_get_resource_list(device_t dev, device_t child)
2004ad7e9b0SAdrian Chadd {
2014ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo = device_get_ivars(child);
2024ad7e9b0SAdrian Chadd 	return (&dinfo->resources);
2034ad7e9b0SAdrian Chadd }
2044ad7e9b0SAdrian Chadd 
2054ad7e9b0SAdrian Chadd static int
2068a03f98aSLandon J. Fuller bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
2078a03f98aSLandon J. Fuller {
2088a03f98aSLandon J. Fuller 	uint32_t	value;
2098a03f98aSLandon J. Fuller 	int		error;
2108a03f98aSLandon J. Fuller 
2118a03f98aSLandon J. Fuller 	if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
2128a03f98aSLandon J. Fuller 		return (error);
2138a03f98aSLandon J. Fuller 
2148a03f98aSLandon J. Fuller 	/* Return only the bottom 16 bits */
2158a03f98aSLandon J. Fuller 	*iost = (value & BCMA_DMP_IOST_MASK);
2168a03f98aSLandon J. Fuller 	return (0);
2178a03f98aSLandon J. Fuller }
2188a03f98aSLandon J. Fuller 
2198a03f98aSLandon J. Fuller static int
2208a03f98aSLandon J. Fuller bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
2218a03f98aSLandon J. Fuller {
2228a03f98aSLandon J. Fuller 	uint32_t	value;
2238a03f98aSLandon J. Fuller 	int		error;
2248a03f98aSLandon J. Fuller 
2258a03f98aSLandon J. Fuller 	if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
2268a03f98aSLandon J. Fuller 		return (error);
2278a03f98aSLandon J. Fuller 
2288a03f98aSLandon J. Fuller 	/* Return only the bottom 16 bits */
2298a03f98aSLandon J. Fuller 	*ioctl = (value & BCMA_DMP_IOCTRL_MASK);
2308a03f98aSLandon J. Fuller 	return (0);
2318a03f98aSLandon J. Fuller }
2328a03f98aSLandon J. Fuller 
2338a03f98aSLandon J. Fuller static int
2348a03f98aSLandon J. Fuller bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
2354ad7e9b0SAdrian Chadd {
2364ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2378a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
2388a03f98aSLandon J. Fuller 	uint32_t		 ioctl;
2394ad7e9b0SAdrian Chadd 
2404ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2418a03f98aSLandon J. Fuller 		return (EINVAL);
2424ad7e9b0SAdrian Chadd 
2434ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2448a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
2454ad7e9b0SAdrian Chadd 		return (ENODEV);
2464ad7e9b0SAdrian Chadd 
2478a03f98aSLandon J. Fuller 	/* Write new value */
2488a03f98aSLandon J. Fuller 	ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
2498a03f98aSLandon J. Fuller 	ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
2508a03f98aSLandon J. Fuller 	ioctl |= (value & mask);
2514ad7e9b0SAdrian Chadd 
2528a03f98aSLandon J. Fuller 	bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
25331318f07SAdrian Chadd 
2548a03f98aSLandon J. Fuller 	/* Perform read-back and wait for completion */
2558a03f98aSLandon J. Fuller 	bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
25631318f07SAdrian Chadd 	DELAY(10);
25731318f07SAdrian Chadd 
25831318f07SAdrian Chadd 	return (0);
2594ad7e9b0SAdrian Chadd }
2604ad7e9b0SAdrian Chadd 
2618a03f98aSLandon J. Fuller static bool
2628a03f98aSLandon J. Fuller bcma_is_hw_suspended(device_t dev, device_t child)
2638a03f98aSLandon J. Fuller {
2648a03f98aSLandon J. Fuller 	uint32_t	rst;
2658a03f98aSLandon J. Fuller 	uint16_t	ioctl;
2668a03f98aSLandon J. Fuller 	int		error;
2678a03f98aSLandon J. Fuller 
2688a03f98aSLandon J. Fuller 	/* Is core held in RESET? */
2698a03f98aSLandon J. Fuller 	error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
2708a03f98aSLandon J. Fuller 	if (error) {
2718a03f98aSLandon J. Fuller 		device_printf(child, "error reading HW reset state: %d\n",
2728a03f98aSLandon J. Fuller 		    error);
2738a03f98aSLandon J. Fuller 		return (true);
2748a03f98aSLandon J. Fuller 	}
2758a03f98aSLandon J. Fuller 
276ba3eb10dSMichael Zhilin 	if (rst & BCMA_DMP_RC_RESET)
2778a03f98aSLandon J. Fuller 		return (true);
2788a03f98aSLandon J. Fuller 
2798a03f98aSLandon J. Fuller 	/* Is core clocked? */
2808a03f98aSLandon J. Fuller 	error = bhnd_read_ioctl(child, &ioctl);
2818a03f98aSLandon J. Fuller 	if (error) {
2828a03f98aSLandon J. Fuller 		device_printf(child, "error reading HW ioctl register: %d\n",
2838a03f98aSLandon J. Fuller 		    error);
2848a03f98aSLandon J. Fuller 		return (true);
2858a03f98aSLandon J. Fuller 	}
2868a03f98aSLandon J. Fuller 
2878a03f98aSLandon J. Fuller 	if (!(ioctl & BHND_IOCTL_CLK_EN))
2888a03f98aSLandon J. Fuller 		return (true);
2898a03f98aSLandon J. Fuller 
2908a03f98aSLandon J. Fuller 	return (false);
2918a03f98aSLandon J. Fuller }
2928a03f98aSLandon J. Fuller 
2934ad7e9b0SAdrian Chadd static int
294ac59515bSLandon J. Fuller bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl,
295ac59515bSLandon J. Fuller     uint16_t reset_ioctl)
2964ad7e9b0SAdrian Chadd {
2974ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2988a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
299ac59515bSLandon J. Fuller 	uint16_t		 clkflags;
3008a03f98aSLandon J. Fuller 	int			 error;
3014ad7e9b0SAdrian Chadd 
3024ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
3038a03f98aSLandon J. Fuller 		return (EINVAL);
3044ad7e9b0SAdrian Chadd 
3054ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
3068a03f98aSLandon J. Fuller 
307ac59515bSLandon J. Fuller 	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
308ac59515bSLandon J. Fuller 	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
309ac59515bSLandon J. Fuller 	if (ioctl & clkflags)
3108a03f98aSLandon J. Fuller 		return (EINVAL);
3114ad7e9b0SAdrian Chadd 
3124ad7e9b0SAdrian Chadd 	/* Can't suspend the core without access to the agent registers */
3138a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3144ad7e9b0SAdrian Chadd 		return (ENODEV);
3154ad7e9b0SAdrian Chadd 
3168a03f98aSLandon J. Fuller 	/* Place core into known RESET state */
317ac59515bSLandon J. Fuller 	if ((error = bhnd_suspend_hw(child, reset_ioctl)))
3188a03f98aSLandon J. Fuller 		return (error);
3194ad7e9b0SAdrian Chadd 
3208a03f98aSLandon J. Fuller 	/*
3218a03f98aSLandon J. Fuller 	 * Leaving the core in reset:
3228a03f98aSLandon J. Fuller 	 * - Set the caller's IOCTL flags
3238a03f98aSLandon J. Fuller 	 * - Enable clocks
3248a03f98aSLandon J. Fuller 	 * - Force clock distribution to ensure propagation throughout the
3258a03f98aSLandon J. Fuller 	 *   core.
3268a03f98aSLandon J. Fuller 	 */
327ac59515bSLandon J. Fuller 	if ((error = bhnd_write_ioctl(child, ioctl | clkflags, UINT16_MAX)))
3288a03f98aSLandon J. Fuller 		return (error);
3298a03f98aSLandon J. Fuller 
3308a03f98aSLandon J. Fuller 	/* Bring the core out of reset */
3318a03f98aSLandon J. Fuller 	if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
3328a03f98aSLandon J. Fuller 		return (error);
3338a03f98aSLandon J. Fuller 
3348a03f98aSLandon J. Fuller 	/* Disable forced clock gating (leaving clock enabled) */
3358a03f98aSLandon J. Fuller 	error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
3368a03f98aSLandon J. Fuller 	if (error)
3378a03f98aSLandon J. Fuller 		return (error);
3388a03f98aSLandon J. Fuller 
3398a03f98aSLandon J. Fuller 	return (0);
3404ad7e9b0SAdrian Chadd }
3414ad7e9b0SAdrian Chadd 
3428a03f98aSLandon J. Fuller static int
343ac59515bSLandon J. Fuller bcma_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
344f90f4b65SLandon J. Fuller {
345f90f4b65SLandon J. Fuller 	struct bcma_devinfo	*dinfo;
346f90f4b65SLandon J. Fuller 	struct bhnd_resource	*r;
347ac59515bSLandon J. Fuller 	uint16_t		 clkflags;
3488a03f98aSLandon J. Fuller 	int			 error;
349f90f4b65SLandon J. Fuller 
350f90f4b65SLandon J. Fuller 	if (device_get_parent(child) != dev)
3518a03f98aSLandon J. Fuller 		return (EINVAL);
352f90f4b65SLandon J. Fuller 
353f90f4b65SLandon J. Fuller 	dinfo = device_get_ivars(child);
3548a03f98aSLandon J. Fuller 
355ac59515bSLandon J. Fuller 	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
356ac59515bSLandon J. Fuller 	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
357ac59515bSLandon J. Fuller 	if (ioctl & clkflags)
358ac59515bSLandon J. Fuller 		return (EINVAL);
359ac59515bSLandon J. Fuller 
3608a03f98aSLandon J. Fuller 	/* Can't suspend the core without access to the agent registers */
361f90f4b65SLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3628a03f98aSLandon J. Fuller 		return (ENODEV);
363f90f4b65SLandon J. Fuller 
3648a03f98aSLandon J. Fuller 	/* Wait for any pending reset operations to clear */
3658a03f98aSLandon J. Fuller 	if ((error = bcma_dmp_wait_reset(child, dinfo)))
3668a03f98aSLandon J. Fuller 		return (error);
367f90f4b65SLandon J. Fuller 
368ac59515bSLandon J. Fuller 	/* Put core into reset (if not already in reset) */
369ba3eb10dSMichael Zhilin 	if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
3708a03f98aSLandon J. Fuller 		return (error);
3718a03f98aSLandon J. Fuller 
372ac59515bSLandon J. Fuller 	/* Write core flags (and clear CLK_EN/CLK_FORCE) */
373ac59515bSLandon J. Fuller 	if ((error = bhnd_write_ioctl(child, ioctl, ~clkflags)))
3748a03f98aSLandon J. Fuller 		return (error);
3758a03f98aSLandon J. Fuller 
3768a03f98aSLandon J. Fuller 	return (0);
3778a03f98aSLandon J. Fuller }
3788a03f98aSLandon J. Fuller 
3798a03f98aSLandon J. Fuller static int
3808a03f98aSLandon J. Fuller bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
381f90f4b65SLandon J. Fuller     u_int width)
382f90f4b65SLandon J. Fuller {
383f90f4b65SLandon J. Fuller 	struct bcma_devinfo	*dinfo;
384f90f4b65SLandon J. Fuller 	struct bhnd_resource	*r;
385f90f4b65SLandon J. Fuller 
386f90f4b65SLandon J. Fuller 	/* Must be a directly attached child core */
387f90f4b65SLandon J. Fuller 	if (device_get_parent(child) != dev)
3888a03f98aSLandon J. Fuller 		return (EINVAL);
389f90f4b65SLandon J. Fuller 
390f90f4b65SLandon J. Fuller 	/* Fetch the agent registers */
391f90f4b65SLandon J. Fuller 	dinfo = device_get_ivars(child);
392f90f4b65SLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3938a03f98aSLandon J. Fuller 		return (ENODEV);
394f90f4b65SLandon J. Fuller 
395f90f4b65SLandon J. Fuller 	/* Verify bounds */
396f90f4b65SLandon J. Fuller 	if (offset > rman_get_size(r->res))
3978a03f98aSLandon J. Fuller 		return (EFAULT);
398f90f4b65SLandon J. Fuller 
399f90f4b65SLandon J. Fuller 	if (rman_get_size(r->res) - offset < width)
4008a03f98aSLandon J. Fuller 		return (EFAULT);
401f90f4b65SLandon J. Fuller 
402f90f4b65SLandon J. Fuller 	switch (width) {
403f90f4b65SLandon J. Fuller 	case 1:
4048a03f98aSLandon J. Fuller 		*((uint8_t *)value) = bhnd_bus_read_1(r, offset);
4058a03f98aSLandon J. Fuller 		return (0);
406f90f4b65SLandon J. Fuller 	case 2:
4078a03f98aSLandon J. Fuller 		*((uint16_t *)value) = bhnd_bus_read_2(r, offset);
4088a03f98aSLandon J. Fuller 		return (0);
409f90f4b65SLandon J. Fuller 	case 4:
4108a03f98aSLandon J. Fuller 		*((uint32_t *)value) = bhnd_bus_read_4(r, offset);
4118a03f98aSLandon J. Fuller 		return (0);
412f90f4b65SLandon J. Fuller 	default:
4138a03f98aSLandon J. Fuller 		return (EINVAL);
4148a03f98aSLandon J. Fuller 	}
4158a03f98aSLandon J. Fuller }
4168a03f98aSLandon J. Fuller 
4178a03f98aSLandon J. Fuller static int
4188a03f98aSLandon J. Fuller bcma_write_config(device_t dev, device_t child, bus_size_t offset,
4198a03f98aSLandon J. Fuller     const void *value, u_int width)
4208a03f98aSLandon J. Fuller {
4218a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
4228a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
4238a03f98aSLandon J. Fuller 
4248a03f98aSLandon J. Fuller 	/* Must be a directly attached child core */
4258a03f98aSLandon J. Fuller 	if (device_get_parent(child) != dev)
4268a03f98aSLandon J. Fuller 		return (EINVAL);
4278a03f98aSLandon J. Fuller 
4288a03f98aSLandon J. Fuller 	/* Fetch the agent registers */
4298a03f98aSLandon J. Fuller 	dinfo = device_get_ivars(child);
4308a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
4318a03f98aSLandon J. Fuller 		return (ENODEV);
4328a03f98aSLandon J. Fuller 
4338a03f98aSLandon J. Fuller 	/* Verify bounds */
4348a03f98aSLandon J. Fuller 	if (offset > rman_get_size(r->res))
4358a03f98aSLandon J. Fuller 		return (EFAULT);
4368a03f98aSLandon J. Fuller 
4378a03f98aSLandon J. Fuller 	if (rman_get_size(r->res) - offset < width)
4388a03f98aSLandon J. Fuller 		return (EFAULT);
4398a03f98aSLandon J. Fuller 
4408a03f98aSLandon J. Fuller 	switch (width) {
4418a03f98aSLandon J. Fuller 	case 1:
4428a03f98aSLandon J. Fuller 		bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
4438a03f98aSLandon J. Fuller 		return (0);
4448a03f98aSLandon J. Fuller 	case 2:
4458a03f98aSLandon J. Fuller 		bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
4468a03f98aSLandon J. Fuller 		return (0);
4478a03f98aSLandon J. Fuller 	case 4:
4488a03f98aSLandon J. Fuller 		bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
4498a03f98aSLandon J. Fuller 		return (0);
4508a03f98aSLandon J. Fuller 	default:
4518a03f98aSLandon J. Fuller 		return (EINVAL);
452f90f4b65SLandon J. Fuller 	}
453f90f4b65SLandon J. Fuller }
454f90f4b65SLandon J. Fuller 
4554ad7e9b0SAdrian Chadd static u_int
4564ad7e9b0SAdrian Chadd bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
4574ad7e9b0SAdrian Chadd {
4584ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo;
4594ad7e9b0SAdrian Chadd 
4604ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
4614ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
4624ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
4634ad7e9b0SAdrian Chadd 		    type));
4644ad7e9b0SAdrian Chadd 
4654ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
4664ad7e9b0SAdrian Chadd 	switch (type) {
4674ad7e9b0SAdrian Chadd 	case BHND_PORT_DEVICE:
4684ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_dev_ports);
4694ad7e9b0SAdrian Chadd 	case BHND_PORT_BRIDGE:
4704ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_bridge_ports);
4714ad7e9b0SAdrian Chadd 	case BHND_PORT_AGENT:
4724ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_wrapper_ports);
473988fa8d0SAdrian Chadd 	default:
474988fa8d0SAdrian Chadd 		device_printf(dev, "%s: unknown type (%d)\n",
475988fa8d0SAdrian Chadd 		    __func__,
476988fa8d0SAdrian Chadd 		    type);
477988fa8d0SAdrian Chadd 		return (0);
4784ad7e9b0SAdrian Chadd 	}
4794ad7e9b0SAdrian Chadd }
4804ad7e9b0SAdrian Chadd 
4814ad7e9b0SAdrian Chadd static u_int
4824ad7e9b0SAdrian Chadd bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
4834ad7e9b0SAdrian Chadd     u_int port_num)
4844ad7e9b0SAdrian Chadd {
4854ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
4864ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
4874ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
4884ad7e9b0SAdrian Chadd 
4894ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
4904ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
4914ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
4924ad7e9b0SAdrian Chadd 		    type, port_num));
4934ad7e9b0SAdrian Chadd 
4944ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
4954ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
4964ad7e9b0SAdrian Chadd 
4974ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
4984ad7e9b0SAdrian Chadd 		if (port->sp_num == port_num)
4994ad7e9b0SAdrian Chadd 			return (port->sp_num_maps);
5004ad7e9b0SAdrian Chadd 	}
5014ad7e9b0SAdrian Chadd 
5024ad7e9b0SAdrian Chadd 	/* not found */
5034ad7e9b0SAdrian Chadd 	return (0);
5044ad7e9b0SAdrian Chadd }
5054ad7e9b0SAdrian Chadd 
5064ad7e9b0SAdrian Chadd static int
5074ad7e9b0SAdrian Chadd bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
5084ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num)
5094ad7e9b0SAdrian Chadd {
5104ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5114ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5124ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5134ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5144ad7e9b0SAdrian Chadd 
5154ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5164ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
5174ad7e9b0SAdrian Chadd 
5184ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
5194ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
5204ad7e9b0SAdrian Chadd 			continue;
5214ad7e9b0SAdrian Chadd 
5224ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
5234ad7e9b0SAdrian Chadd 			if (map->m_region_num == region_num)
5244ad7e9b0SAdrian Chadd 				return map->m_rid;
5254ad7e9b0SAdrian Chadd 	}
5264ad7e9b0SAdrian Chadd 
5274ad7e9b0SAdrian Chadd 	return -1;
5284ad7e9b0SAdrian Chadd }
5294ad7e9b0SAdrian Chadd 
5304ad7e9b0SAdrian Chadd static int
5314ad7e9b0SAdrian Chadd bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
5324ad7e9b0SAdrian Chadd     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
5334ad7e9b0SAdrian Chadd {
5344ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5354ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5364ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5374ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5384ad7e9b0SAdrian Chadd 
5394ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5404ad7e9b0SAdrian Chadd 
5414ad7e9b0SAdrian Chadd 	/* Ports are always memory mapped */
5424ad7e9b0SAdrian Chadd 	if (type != SYS_RES_MEMORY)
5434ad7e9b0SAdrian Chadd 		return (EINVAL);
5444ad7e9b0SAdrian Chadd 
5454ad7e9b0SAdrian Chadd 	/* Starting with the most likely device list, search all three port
5464ad7e9b0SAdrian Chadd 	 * lists */
5474ad7e9b0SAdrian Chadd 	bhnd_port_type types[] = {
5484ad7e9b0SAdrian Chadd 	    BHND_PORT_DEVICE,
5494ad7e9b0SAdrian Chadd 	    BHND_PORT_AGENT,
5504ad7e9b0SAdrian Chadd 	    BHND_PORT_BRIDGE
5514ad7e9b0SAdrian Chadd 	};
5524ad7e9b0SAdrian Chadd 
5534ad7e9b0SAdrian Chadd 	for (int i = 0; i < nitems(types); i++) {
5544ad7e9b0SAdrian Chadd 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
5554ad7e9b0SAdrian Chadd 
5564ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(port, ports, sp_link) {
5574ad7e9b0SAdrian Chadd 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
5584ad7e9b0SAdrian Chadd 				if (map->m_rid != rid)
5594ad7e9b0SAdrian Chadd 					continue;
5604ad7e9b0SAdrian Chadd 
5614ad7e9b0SAdrian Chadd 				*port_type = port->sp_type;
5624ad7e9b0SAdrian Chadd 				*port_num = port->sp_num;
5634ad7e9b0SAdrian Chadd 				*region_num = map->m_region_num;
5644ad7e9b0SAdrian Chadd 				return (0);
5654ad7e9b0SAdrian Chadd 			}
5664ad7e9b0SAdrian Chadd 		}
5674ad7e9b0SAdrian Chadd 	}
5684ad7e9b0SAdrian Chadd 
5694ad7e9b0SAdrian Chadd 	return (ENOENT);
5704ad7e9b0SAdrian Chadd }
5714ad7e9b0SAdrian Chadd 
5724ad7e9b0SAdrian Chadd static int
5734ad7e9b0SAdrian Chadd bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
5744ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
5754ad7e9b0SAdrian Chadd {
5764ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5774ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5784ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5794ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5804ad7e9b0SAdrian Chadd 
5814ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5824ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
5834ad7e9b0SAdrian Chadd 
5844ad7e9b0SAdrian Chadd 	/* Search the port list */
5854ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
5864ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
5874ad7e9b0SAdrian Chadd 			continue;
5884ad7e9b0SAdrian Chadd 
5894ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
5904ad7e9b0SAdrian Chadd 			if (map->m_region_num != region_num)
5914ad7e9b0SAdrian Chadd 				continue;
5924ad7e9b0SAdrian Chadd 
5934ad7e9b0SAdrian Chadd 			/* Found! */
5944ad7e9b0SAdrian Chadd 			*addr = map->m_base;
5954ad7e9b0SAdrian Chadd 			*size = map->m_size;
5964ad7e9b0SAdrian Chadd 			return (0);
5974ad7e9b0SAdrian Chadd 		}
5984ad7e9b0SAdrian Chadd 	}
5994ad7e9b0SAdrian Chadd 
6004ad7e9b0SAdrian Chadd 	return (ENOENT);
6014ad7e9b0SAdrian Chadd }
6024ad7e9b0SAdrian Chadd 
603824b48efSLandon J. Fuller /**
604824b48efSLandon J. Fuller  * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
605824b48efSLandon J. Fuller  */
606caeff9a3SLandon J. Fuller u_int
607824b48efSLandon J. Fuller bcma_get_intr_count(device_t dev, device_t child)
608824b48efSLandon J. Fuller {
609824b48efSLandon J. Fuller 	struct bcma_devinfo *dinfo;
610caeff9a3SLandon J. Fuller 
611caeff9a3SLandon J. Fuller 	/* delegate non-bus-attached devices to our parent */
612caeff9a3SLandon J. Fuller 	if (device_get_parent(child) != dev)
613caeff9a3SLandon J. Fuller 		return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
614824b48efSLandon J. Fuller 
615824b48efSLandon J. Fuller 	dinfo = device_get_ivars(child);
616caeff9a3SLandon J. Fuller 	return (dinfo->num_intrs);
617824b48efSLandon J. Fuller }
618824b48efSLandon J. Fuller 
619824b48efSLandon J. Fuller /**
620caeff9a3SLandon J. Fuller  * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
621824b48efSLandon J. Fuller  */
622824b48efSLandon J. Fuller int
623caeff9a3SLandon J. Fuller bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
624824b48efSLandon J. Fuller {
625824b48efSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
626caeff9a3SLandon J. Fuller 	struct bcma_intr	*desc;
627caeff9a3SLandon J. Fuller 
628caeff9a3SLandon J. Fuller 	/* delegate non-bus-attached devices to our parent */
629caeff9a3SLandon J. Fuller 	if (device_get_parent(child) != dev) {
630caeff9a3SLandon J. Fuller 		return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
631caeff9a3SLandon J. Fuller 		    intr, ivec));
632caeff9a3SLandon J. Fuller 	}
633824b48efSLandon J. Fuller 
634824b48efSLandon J. Fuller 	dinfo = device_get_ivars(child);
635824b48efSLandon J. Fuller 
636caeff9a3SLandon J. Fuller 	STAILQ_FOREACH(desc, &dinfo->intrs, i_link) {
637caeff9a3SLandon J. Fuller 		if (desc->i_sel == intr) {
638caeff9a3SLandon J. Fuller 			*ivec = desc->i_busline;
639824b48efSLandon J. Fuller 			return (0);
640824b48efSLandon J. Fuller 		}
641caeff9a3SLandon J. Fuller 	}
642caeff9a3SLandon J. Fuller 
643caeff9a3SLandon J. Fuller 	/* Not found */
644caeff9a3SLandon J. Fuller 	return (ENXIO);
645caeff9a3SLandon J. Fuller }
646824b48efSLandon J. Fuller 
6474ad7e9b0SAdrian Chadd /**
648664a7497SLandon J. Fuller  * Scan the device enumeration ROM table, adding all valid discovered cores to
6494ad7e9b0SAdrian Chadd  * the bus.
6504ad7e9b0SAdrian Chadd  *
6514ad7e9b0SAdrian Chadd  * @param bus The bcma bus.
6524ad7e9b0SAdrian Chadd  */
6534ad7e9b0SAdrian Chadd int
654664a7497SLandon J. Fuller bcma_add_children(device_t bus)
6554ad7e9b0SAdrian Chadd {
656664a7497SLandon J. Fuller 	bhnd_erom_t			*erom;
657664a7497SLandon J. Fuller 	struct bcma_erom		*bcma_erom;
65889294a78SLandon J. Fuller 	struct bhnd_erom_io		*eio;
659664a7497SLandon J. Fuller 	const struct bhnd_chipid	*cid;
6604ad7e9b0SAdrian Chadd 	struct bcma_corecfg		*corecfg;
6614ad7e9b0SAdrian Chadd 	struct bcma_devinfo		*dinfo;
6624ad7e9b0SAdrian Chadd 	device_t			 child;
6634ad7e9b0SAdrian Chadd 	int				 error;
6644ad7e9b0SAdrian Chadd 
665664a7497SLandon J. Fuller 	cid = BHND_BUS_GET_CHIPID(bus, bus);
6664ad7e9b0SAdrian Chadd 	corecfg = NULL;
6674ad7e9b0SAdrian Chadd 
668664a7497SLandon J. Fuller 	/* Allocate our EROM parser */
66989294a78SLandon J. Fuller 	eio = bhnd_erom_iores_new(bus, BCMA_EROM_RID);
67089294a78SLandon J. Fuller 	erom = bhnd_erom_alloc(&bcma_erom_parser, cid, eio);
67189294a78SLandon J. Fuller 	if (erom == NULL) {
67289294a78SLandon J. Fuller 		bhnd_erom_io_fini(eio);
673664a7497SLandon J. Fuller 		return (ENODEV);
67489294a78SLandon J. Fuller 	}
6754ad7e9b0SAdrian Chadd 
6764ad7e9b0SAdrian Chadd 	/* Add all cores. */
677664a7497SLandon J. Fuller 	bcma_erom = (struct bcma_erom *)erom;
678664a7497SLandon J. Fuller 	while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
6794ad7e9b0SAdrian Chadd 		/* Add the child device */
680*a05a6804SWarner Losh 		child = BUS_ADD_CHILD(bus, 0, NULL, DEVICE_UNIT_ANY);
6814ad7e9b0SAdrian Chadd 		if (child == NULL) {
6824ad7e9b0SAdrian Chadd 			error = ENXIO;
683111d7cb2SLandon J. Fuller 			goto cleanup;
6844ad7e9b0SAdrian Chadd 		}
6854ad7e9b0SAdrian Chadd 
686688fc8c0SLandon J. Fuller 		/* Initialize device ivars */
687688fc8c0SLandon J. Fuller 		dinfo = device_get_ivars(child);
688caeff9a3SLandon J. Fuller 		if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg)))
689111d7cb2SLandon J. Fuller 			goto cleanup;
690688fc8c0SLandon J. Fuller 
691688fc8c0SLandon J. Fuller 		/* The dinfo instance now owns the corecfg value */
692688fc8c0SLandon J. Fuller 		corecfg = NULL;
6934ad7e9b0SAdrian Chadd 
6944ad7e9b0SAdrian Chadd 		/* If pins are floating or the hardware is otherwise
6954ad7e9b0SAdrian Chadd 		 * unpopulated, the device shouldn't be used. */
6964ad7e9b0SAdrian Chadd 		if (bhnd_is_hw_disabled(child))
6974ad7e9b0SAdrian Chadd 			device_disable(child);
698f90f4b65SLandon J. Fuller 
699f90f4b65SLandon J. Fuller 		/* Issue bus callback for fully initialized child. */
700f90f4b65SLandon J. Fuller 		BHND_BUS_CHILD_ADDED(bus, child);
7014ad7e9b0SAdrian Chadd 	}
7024ad7e9b0SAdrian Chadd 
703111d7cb2SLandon J. Fuller 	/* EOF while parsing cores is expected */
7044ad7e9b0SAdrian Chadd 	if (error == ENOENT)
705664a7497SLandon J. Fuller 		error = 0;
7064ad7e9b0SAdrian Chadd 
707111d7cb2SLandon J. Fuller cleanup:
708664a7497SLandon J. Fuller 	bhnd_erom_free(erom);
709664a7497SLandon J. Fuller 
7104ad7e9b0SAdrian Chadd 	if (corecfg != NULL)
7114ad7e9b0SAdrian Chadd 		bcma_free_corecfg(corecfg);
7124ad7e9b0SAdrian Chadd 
713111d7cb2SLandon J. Fuller 	if (error)
714111d7cb2SLandon J. Fuller 		device_delete_children(bus);
715111d7cb2SLandon J. Fuller 
7164ad7e9b0SAdrian Chadd 	return (error);
7174ad7e9b0SAdrian Chadd }
7184ad7e9b0SAdrian Chadd 
7194ad7e9b0SAdrian Chadd static device_method_t bcma_methods[] = {
7204ad7e9b0SAdrian Chadd 	/* Device interface */
7214ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bcma_probe),
7224ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bcma_attach),
7234ad7e9b0SAdrian Chadd 	DEVMETHOD(device_detach,		bcma_detach),
7244ad7e9b0SAdrian Chadd 
7254ad7e9b0SAdrian Chadd 	/* Bus interface */
7268a03f98aSLandon J. Fuller 	DEVMETHOD(bus_add_child,		bcma_add_child),
7278a03f98aSLandon J. Fuller 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
7284ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
7294ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
7304ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
7314ad7e9b0SAdrian Chadd 
7324ad7e9b0SAdrian Chadd 	/* BHND interface */
733111d7cb2SLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_erom_class,	bcma_get_erom_class),
7348a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_ioctl,		bcma_read_ioctl),
7358a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_write_ioctl,		bcma_write_ioctl),
7368a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_iost,		bcma_read_iost),
7378a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_is_hw_suspended,	bcma_is_hw_suspended),
7388a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_reset_hw,		bcma_reset_hw),
7398a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_suspend_hw,		bcma_suspend_hw),
740f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_config,		bcma_read_config),
741f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_write_config,	bcma_write_config),
7424ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
7434ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
7444ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
7454ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
7464ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
747824b48efSLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_intr_count,	bcma_get_intr_count),
748caeff9a3SLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_intr_ivec,	bcma_get_intr_ivec),
7494ad7e9b0SAdrian Chadd 
7504ad7e9b0SAdrian Chadd 	DEVMETHOD_END
7514ad7e9b0SAdrian Chadd };
7524ad7e9b0SAdrian Chadd 
7534ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
7544ad7e9b0SAdrian Chadd MODULE_VERSION(bcma, 1);
7554ad7e9b0SAdrian Chadd MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
756