xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision fdafd315)
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 *
bcma_get_erom_class(driver_t * driver)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
bcma_probe(device_t dev)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
bcma_attach(device_t dev)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))) {
84111d7cb2SLandon J. Fuller 		device_delete_children(dev);
854ad7e9b0SAdrian Chadd 		return (error);
864ad7e9b0SAdrian Chadd 	}
874ad7e9b0SAdrian Chadd 
88111d7cb2SLandon J. Fuller 	return (0);
894ad7e9b0SAdrian Chadd }
904ad7e9b0SAdrian Chadd 
914ad7e9b0SAdrian Chadd int
bcma_detach(device_t dev)924ad7e9b0SAdrian Chadd bcma_detach(device_t dev)
934ad7e9b0SAdrian Chadd {
944ad7e9b0SAdrian Chadd 	return (bhnd_generic_detach(dev));
954ad7e9b0SAdrian Chadd }
964ad7e9b0SAdrian Chadd 
978a03f98aSLandon J. Fuller static device_t
bcma_add_child(device_t dev,u_int order,const char * name,int unit)988a03f98aSLandon J. Fuller bcma_add_child(device_t dev, u_int order, const char *name, int unit)
998a03f98aSLandon J. Fuller {
1008a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
1018a03f98aSLandon J. Fuller 	device_t		 child;
1028a03f98aSLandon J. Fuller 
1038a03f98aSLandon J. Fuller 	child = device_add_child_ordered(dev, order, name, unit);
1048a03f98aSLandon J. Fuller 	if (child == NULL)
1058a03f98aSLandon J. Fuller 		return (NULL);
1068a03f98aSLandon J. Fuller 
1078a03f98aSLandon J. Fuller 	if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
1088a03f98aSLandon J. Fuller 		device_delete_child(dev, child);
1098a03f98aSLandon J. Fuller 		return (NULL);
1108a03f98aSLandon J. Fuller 	}
1118a03f98aSLandon J. Fuller 
1128a03f98aSLandon J. Fuller 	device_set_ivars(child, dinfo);
1138a03f98aSLandon J. Fuller 
1148a03f98aSLandon J. Fuller 	return (child);
1158a03f98aSLandon J. Fuller }
1168a03f98aSLandon J. Fuller 
1178a03f98aSLandon J. Fuller static void
bcma_child_deleted(device_t dev,device_t child)1188a03f98aSLandon J. Fuller bcma_child_deleted(device_t dev, device_t child)
1198a03f98aSLandon J. Fuller {
1208a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
1218a03f98aSLandon J. Fuller 
1228a03f98aSLandon J. Fuller 	/* Call required bhnd(4) implementation */
1238a03f98aSLandon J. Fuller 	bhnd_generic_child_deleted(dev, child);
1248a03f98aSLandon J. Fuller 
1258a03f98aSLandon J. Fuller 	/* Free bcma device info */
1268a03f98aSLandon J. Fuller 	if ((dinfo = device_get_ivars(child)) != NULL)
127caeff9a3SLandon J. Fuller 		bcma_free_dinfo(dev, child, dinfo);
1288a03f98aSLandon J. Fuller 
1298a03f98aSLandon J. Fuller 	device_set_ivars(child, NULL);
1308a03f98aSLandon J. Fuller }
1318a03f98aSLandon J. Fuller 
1324ad7e9b0SAdrian Chadd static int
bcma_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)1334ad7e9b0SAdrian Chadd bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
1344ad7e9b0SAdrian Chadd {
1354ad7e9b0SAdrian Chadd 	const struct bcma_devinfo *dinfo;
1364ad7e9b0SAdrian Chadd 	const struct bhnd_core_info *ci;
1374ad7e9b0SAdrian Chadd 
1384ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
1394ad7e9b0SAdrian Chadd 	ci = &dinfo->corecfg->core_info;
1404ad7e9b0SAdrian Chadd 
1414ad7e9b0SAdrian Chadd 	switch (index) {
1424ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1434ad7e9b0SAdrian Chadd 		*result = ci->vendor;
1444ad7e9b0SAdrian Chadd 		return (0);
1454ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1464ad7e9b0SAdrian Chadd 		*result = ci->device;
1474ad7e9b0SAdrian Chadd 		return (0);
1484ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1494ad7e9b0SAdrian Chadd 		*result = ci->hwrev;
1504ad7e9b0SAdrian Chadd 		return (0);
1514ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1524ad7e9b0SAdrian Chadd 		*result = bhnd_core_class(ci);
1534ad7e9b0SAdrian Chadd 		return (0);
1544ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1554ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
1564ad7e9b0SAdrian Chadd 		return (0);
1574ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1584ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_core_name(ci);
1594ad7e9b0SAdrian Chadd 		return (0);
1604ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1614ad7e9b0SAdrian Chadd 		*result = ci->core_idx;
1624ad7e9b0SAdrian Chadd 		return (0);
1634ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1644ad7e9b0SAdrian Chadd 		*result = ci->unit;
1654ad7e9b0SAdrian Chadd 		return (0);
1668a03f98aSLandon J. Fuller 	case BHND_IVAR_PMU_INFO:
1678a03f98aSLandon J. Fuller 		*result = (uintptr_t) dinfo->pmu_info;
1688a03f98aSLandon J. Fuller 		return (0);
1694ad7e9b0SAdrian Chadd 	default:
1704ad7e9b0SAdrian Chadd 		return (ENOENT);
1714ad7e9b0SAdrian Chadd 	}
1724ad7e9b0SAdrian Chadd }
1734ad7e9b0SAdrian Chadd 
1744ad7e9b0SAdrian Chadd static int
bcma_write_ivar(device_t dev,device_t child,int index,uintptr_t value)1754ad7e9b0SAdrian Chadd bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
1764ad7e9b0SAdrian Chadd {
1778a03f98aSLandon J. Fuller 	struct bcma_devinfo *dinfo;
1788a03f98aSLandon J. Fuller 
1798a03f98aSLandon J. Fuller 	dinfo = device_get_ivars(child);
1808a03f98aSLandon J. Fuller 
1814ad7e9b0SAdrian Chadd 	switch (index) {
1824ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1834ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1844ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1854ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1864ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1874ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1884ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1894ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1904ad7e9b0SAdrian Chadd 		return (EINVAL);
1918a03f98aSLandon J. Fuller 	case BHND_IVAR_PMU_INFO:
1924e96bf3aSLandon J. Fuller 		dinfo->pmu_info = (void *)value;
1938a03f98aSLandon J. Fuller 		return (0);
1944ad7e9b0SAdrian Chadd 	default:
1954ad7e9b0SAdrian Chadd 		return (ENOENT);
1964ad7e9b0SAdrian Chadd 	}
1974ad7e9b0SAdrian Chadd }
1984ad7e9b0SAdrian Chadd 
1994ad7e9b0SAdrian Chadd static struct resource_list *
bcma_get_resource_list(device_t dev,device_t child)2004ad7e9b0SAdrian Chadd bcma_get_resource_list(device_t dev, device_t child)
2014ad7e9b0SAdrian Chadd {
2024ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo = device_get_ivars(child);
2034ad7e9b0SAdrian Chadd 	return (&dinfo->resources);
2044ad7e9b0SAdrian Chadd }
2054ad7e9b0SAdrian Chadd 
2064ad7e9b0SAdrian Chadd static int
bcma_read_iost(device_t dev,device_t child,uint16_t * iost)2078a03f98aSLandon J. Fuller bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
2088a03f98aSLandon J. Fuller {
2098a03f98aSLandon J. Fuller 	uint32_t	value;
2108a03f98aSLandon J. Fuller 	int		error;
2118a03f98aSLandon J. Fuller 
2128a03f98aSLandon J. Fuller 	if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
2138a03f98aSLandon J. Fuller 		return (error);
2148a03f98aSLandon J. Fuller 
2158a03f98aSLandon J. Fuller 	/* Return only the bottom 16 bits */
2168a03f98aSLandon J. Fuller 	*iost = (value & BCMA_DMP_IOST_MASK);
2178a03f98aSLandon J. Fuller 	return (0);
2188a03f98aSLandon J. Fuller }
2198a03f98aSLandon J. Fuller 
2208a03f98aSLandon J. Fuller static int
bcma_read_ioctl(device_t dev,device_t child,uint16_t * ioctl)2218a03f98aSLandon J. Fuller bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
2228a03f98aSLandon J. Fuller {
2238a03f98aSLandon J. Fuller 	uint32_t	value;
2248a03f98aSLandon J. Fuller 	int		error;
2258a03f98aSLandon J. Fuller 
2268a03f98aSLandon J. Fuller 	if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
2278a03f98aSLandon J. Fuller 		return (error);
2288a03f98aSLandon J. Fuller 
2298a03f98aSLandon J. Fuller 	/* Return only the bottom 16 bits */
2308a03f98aSLandon J. Fuller 	*ioctl = (value & BCMA_DMP_IOCTRL_MASK);
2318a03f98aSLandon J. Fuller 	return (0);
2328a03f98aSLandon J. Fuller }
2338a03f98aSLandon J. Fuller 
2348a03f98aSLandon J. Fuller static int
bcma_write_ioctl(device_t dev,device_t child,uint16_t value,uint16_t mask)2358a03f98aSLandon J. Fuller bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
2364ad7e9b0SAdrian Chadd {
2374ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2388a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
2398a03f98aSLandon J. Fuller 	uint32_t		 ioctl;
2404ad7e9b0SAdrian Chadd 
2414ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2428a03f98aSLandon J. Fuller 		return (EINVAL);
2434ad7e9b0SAdrian Chadd 
2444ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2458a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
2464ad7e9b0SAdrian Chadd 		return (ENODEV);
2474ad7e9b0SAdrian Chadd 
2488a03f98aSLandon J. Fuller 	/* Write new value */
2498a03f98aSLandon J. Fuller 	ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
2508a03f98aSLandon J. Fuller 	ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
2518a03f98aSLandon J. Fuller 	ioctl |= (value & mask);
2524ad7e9b0SAdrian Chadd 
2538a03f98aSLandon J. Fuller 	bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
25431318f07SAdrian Chadd 
2558a03f98aSLandon J. Fuller 	/* Perform read-back and wait for completion */
2568a03f98aSLandon J. Fuller 	bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
25731318f07SAdrian Chadd 	DELAY(10);
25831318f07SAdrian Chadd 
25931318f07SAdrian Chadd 	return (0);
2604ad7e9b0SAdrian Chadd }
2614ad7e9b0SAdrian Chadd 
2628a03f98aSLandon J. Fuller static bool
bcma_is_hw_suspended(device_t dev,device_t child)2638a03f98aSLandon J. Fuller bcma_is_hw_suspended(device_t dev, device_t child)
2648a03f98aSLandon J. Fuller {
2658a03f98aSLandon J. Fuller 	uint32_t	rst;
2668a03f98aSLandon J. Fuller 	uint16_t	ioctl;
2678a03f98aSLandon J. Fuller 	int		error;
2688a03f98aSLandon J. Fuller 
2698a03f98aSLandon J. Fuller 	/* Is core held in RESET? */
2708a03f98aSLandon J. Fuller 	error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
2718a03f98aSLandon J. Fuller 	if (error) {
2728a03f98aSLandon J. Fuller 		device_printf(child, "error reading HW reset state: %d\n",
2738a03f98aSLandon J. Fuller 		    error);
2748a03f98aSLandon J. Fuller 		return (true);
2758a03f98aSLandon J. Fuller 	}
2768a03f98aSLandon J. Fuller 
277ba3eb10dSMichael Zhilin 	if (rst & BCMA_DMP_RC_RESET)
2788a03f98aSLandon J. Fuller 		return (true);
2798a03f98aSLandon J. Fuller 
2808a03f98aSLandon J. Fuller 	/* Is core clocked? */
2818a03f98aSLandon J. Fuller 	error = bhnd_read_ioctl(child, &ioctl);
2828a03f98aSLandon J. Fuller 	if (error) {
2838a03f98aSLandon J. Fuller 		device_printf(child, "error reading HW ioctl register: %d\n",
2848a03f98aSLandon J. Fuller 		    error);
2858a03f98aSLandon J. Fuller 		return (true);
2868a03f98aSLandon J. Fuller 	}
2878a03f98aSLandon J. Fuller 
2888a03f98aSLandon J. Fuller 	if (!(ioctl & BHND_IOCTL_CLK_EN))
2898a03f98aSLandon J. Fuller 		return (true);
2908a03f98aSLandon J. Fuller 
2918a03f98aSLandon J. Fuller 	return (false);
2928a03f98aSLandon J. Fuller }
2938a03f98aSLandon J. Fuller 
2944ad7e9b0SAdrian Chadd static int
bcma_reset_hw(device_t dev,device_t child,uint16_t ioctl,uint16_t reset_ioctl)295ac59515bSLandon J. Fuller bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl,
296ac59515bSLandon J. Fuller     uint16_t reset_ioctl)
2974ad7e9b0SAdrian Chadd {
2984ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2998a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
300ac59515bSLandon J. Fuller 	uint16_t		 clkflags;
3018a03f98aSLandon J. Fuller 	int			 error;
3024ad7e9b0SAdrian Chadd 
3034ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
3048a03f98aSLandon J. Fuller 		return (EINVAL);
3054ad7e9b0SAdrian Chadd 
3064ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
3078a03f98aSLandon J. Fuller 
308ac59515bSLandon J. Fuller 	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
309ac59515bSLandon J. Fuller 	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
310ac59515bSLandon J. Fuller 	if (ioctl & clkflags)
3118a03f98aSLandon J. Fuller 		return (EINVAL);
3124ad7e9b0SAdrian Chadd 
3134ad7e9b0SAdrian Chadd 	/* Can't suspend the core without access to the agent registers */
3148a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3154ad7e9b0SAdrian Chadd 		return (ENODEV);
3164ad7e9b0SAdrian Chadd 
3178a03f98aSLandon J. Fuller 	/* Place core into known RESET state */
318ac59515bSLandon J. Fuller 	if ((error = bhnd_suspend_hw(child, reset_ioctl)))
3198a03f98aSLandon J. Fuller 		return (error);
3204ad7e9b0SAdrian Chadd 
3218a03f98aSLandon J. Fuller 	/*
3228a03f98aSLandon J. Fuller 	 * Leaving the core in reset:
3238a03f98aSLandon J. Fuller 	 * - Set the caller's IOCTL flags
3248a03f98aSLandon J. Fuller 	 * - Enable clocks
3258a03f98aSLandon J. Fuller 	 * - Force clock distribution to ensure propagation throughout the
3268a03f98aSLandon J. Fuller 	 *   core.
3278a03f98aSLandon J. Fuller 	 */
328ac59515bSLandon J. Fuller 	if ((error = bhnd_write_ioctl(child, ioctl | clkflags, UINT16_MAX)))
3298a03f98aSLandon J. Fuller 		return (error);
3308a03f98aSLandon J. Fuller 
3318a03f98aSLandon J. Fuller 	/* Bring the core out of reset */
3328a03f98aSLandon J. Fuller 	if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
3338a03f98aSLandon J. Fuller 		return (error);
3348a03f98aSLandon J. Fuller 
3358a03f98aSLandon J. Fuller 	/* Disable forced clock gating (leaving clock enabled) */
3368a03f98aSLandon J. Fuller 	error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
3378a03f98aSLandon J. Fuller 	if (error)
3388a03f98aSLandon J. Fuller 		return (error);
3398a03f98aSLandon J. Fuller 
3408a03f98aSLandon J. Fuller 	return (0);
3414ad7e9b0SAdrian Chadd }
3424ad7e9b0SAdrian Chadd 
3438a03f98aSLandon J. Fuller static int
bcma_suspend_hw(device_t dev,device_t child,uint16_t ioctl)344ac59515bSLandon J. Fuller bcma_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
345f90f4b65SLandon J. Fuller {
346f90f4b65SLandon J. Fuller 	struct bcma_devinfo	*dinfo;
347f90f4b65SLandon J. Fuller 	struct bhnd_resource	*r;
348ac59515bSLandon J. Fuller 	uint16_t		 clkflags;
3498a03f98aSLandon J. Fuller 	int			 error;
350f90f4b65SLandon J. Fuller 
351f90f4b65SLandon J. Fuller 	if (device_get_parent(child) != dev)
3528a03f98aSLandon J. Fuller 		return (EINVAL);
353f90f4b65SLandon J. Fuller 
354f90f4b65SLandon J. Fuller 	dinfo = device_get_ivars(child);
3558a03f98aSLandon J. Fuller 
356ac59515bSLandon J. Fuller 	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
357ac59515bSLandon J. Fuller 	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
358ac59515bSLandon J. Fuller 	if (ioctl & clkflags)
359ac59515bSLandon J. Fuller 		return (EINVAL);
360ac59515bSLandon J. Fuller 
3618a03f98aSLandon J. Fuller 	/* Can't suspend the core without access to the agent registers */
362f90f4b65SLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3638a03f98aSLandon J. Fuller 		return (ENODEV);
364f90f4b65SLandon J. Fuller 
3658a03f98aSLandon J. Fuller 	/* Wait for any pending reset operations to clear */
3668a03f98aSLandon J. Fuller 	if ((error = bcma_dmp_wait_reset(child, dinfo)))
3678a03f98aSLandon J. Fuller 		return (error);
368f90f4b65SLandon J. Fuller 
369ac59515bSLandon J. Fuller 	/* Put core into reset (if not already in reset) */
370ba3eb10dSMichael Zhilin 	if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
3718a03f98aSLandon J. Fuller 		return (error);
3728a03f98aSLandon J. Fuller 
373ac59515bSLandon J. Fuller 	/* Write core flags (and clear CLK_EN/CLK_FORCE) */
374ac59515bSLandon J. Fuller 	if ((error = bhnd_write_ioctl(child, ioctl, ~clkflags)))
3758a03f98aSLandon J. Fuller 		return (error);
3768a03f98aSLandon J. Fuller 
3778a03f98aSLandon J. Fuller 	return (0);
3788a03f98aSLandon J. Fuller }
3798a03f98aSLandon J. Fuller 
3808a03f98aSLandon J. Fuller static int
bcma_read_config(device_t dev,device_t child,bus_size_t offset,void * value,u_int width)3818a03f98aSLandon J. Fuller bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
382f90f4b65SLandon J. Fuller     u_int width)
383f90f4b65SLandon J. Fuller {
384f90f4b65SLandon J. Fuller 	struct bcma_devinfo	*dinfo;
385f90f4b65SLandon J. Fuller 	struct bhnd_resource	*r;
386f90f4b65SLandon J. Fuller 
387f90f4b65SLandon J. Fuller 	/* Must be a directly attached child core */
388f90f4b65SLandon J. Fuller 	if (device_get_parent(child) != dev)
3898a03f98aSLandon J. Fuller 		return (EINVAL);
390f90f4b65SLandon J. Fuller 
391f90f4b65SLandon J. Fuller 	/* Fetch the agent registers */
392f90f4b65SLandon J. Fuller 	dinfo = device_get_ivars(child);
393f90f4b65SLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
3948a03f98aSLandon J. Fuller 		return (ENODEV);
395f90f4b65SLandon J. Fuller 
396f90f4b65SLandon J. Fuller 	/* Verify bounds */
397f90f4b65SLandon J. Fuller 	if (offset > rman_get_size(r->res))
3988a03f98aSLandon J. Fuller 		return (EFAULT);
399f90f4b65SLandon J. Fuller 
400f90f4b65SLandon J. Fuller 	if (rman_get_size(r->res) - offset < width)
4018a03f98aSLandon J. Fuller 		return (EFAULT);
402f90f4b65SLandon J. Fuller 
403f90f4b65SLandon J. Fuller 	switch (width) {
404f90f4b65SLandon J. Fuller 	case 1:
4058a03f98aSLandon J. Fuller 		*((uint8_t *)value) = bhnd_bus_read_1(r, offset);
4068a03f98aSLandon J. Fuller 		return (0);
407f90f4b65SLandon J. Fuller 	case 2:
4088a03f98aSLandon J. Fuller 		*((uint16_t *)value) = bhnd_bus_read_2(r, offset);
4098a03f98aSLandon J. Fuller 		return (0);
410f90f4b65SLandon J. Fuller 	case 4:
4118a03f98aSLandon J. Fuller 		*((uint32_t *)value) = bhnd_bus_read_4(r, offset);
4128a03f98aSLandon J. Fuller 		return (0);
413f90f4b65SLandon J. Fuller 	default:
4148a03f98aSLandon J. Fuller 		return (EINVAL);
4158a03f98aSLandon J. Fuller 	}
4168a03f98aSLandon J. Fuller }
4178a03f98aSLandon J. Fuller 
4188a03f98aSLandon J. Fuller static int
bcma_write_config(device_t dev,device_t child,bus_size_t offset,const void * value,u_int width)4198a03f98aSLandon J. Fuller bcma_write_config(device_t dev, device_t child, bus_size_t offset,
4208a03f98aSLandon J. Fuller     const void *value, u_int width)
4218a03f98aSLandon J. Fuller {
4228a03f98aSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
4238a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
4248a03f98aSLandon J. Fuller 
4258a03f98aSLandon J. Fuller 	/* Must be a directly attached child core */
4268a03f98aSLandon J. Fuller 	if (device_get_parent(child) != dev)
4278a03f98aSLandon J. Fuller 		return (EINVAL);
4288a03f98aSLandon J. Fuller 
4298a03f98aSLandon J. Fuller 	/* Fetch the agent registers */
4308a03f98aSLandon J. Fuller 	dinfo = device_get_ivars(child);
4318a03f98aSLandon J. Fuller 	if ((r = dinfo->res_agent) == NULL)
4328a03f98aSLandon J. Fuller 		return (ENODEV);
4338a03f98aSLandon J. Fuller 
4348a03f98aSLandon J. Fuller 	/* Verify bounds */
4358a03f98aSLandon J. Fuller 	if (offset > rman_get_size(r->res))
4368a03f98aSLandon J. Fuller 		return (EFAULT);
4378a03f98aSLandon J. Fuller 
4388a03f98aSLandon J. Fuller 	if (rman_get_size(r->res) - offset < width)
4398a03f98aSLandon J. Fuller 		return (EFAULT);
4408a03f98aSLandon J. Fuller 
4418a03f98aSLandon J. Fuller 	switch (width) {
4428a03f98aSLandon J. Fuller 	case 1:
4438a03f98aSLandon J. Fuller 		bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
4448a03f98aSLandon J. Fuller 		return (0);
4458a03f98aSLandon J. Fuller 	case 2:
4468a03f98aSLandon J. Fuller 		bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
4478a03f98aSLandon J. Fuller 		return (0);
4488a03f98aSLandon J. Fuller 	case 4:
4498a03f98aSLandon J. Fuller 		bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
4508a03f98aSLandon J. Fuller 		return (0);
4518a03f98aSLandon J. Fuller 	default:
4528a03f98aSLandon J. Fuller 		return (EINVAL);
453f90f4b65SLandon J. Fuller 	}
454f90f4b65SLandon J. Fuller }
455f90f4b65SLandon J. Fuller 
4564ad7e9b0SAdrian Chadd static u_int
bcma_get_port_count(device_t dev,device_t child,bhnd_port_type type)4574ad7e9b0SAdrian Chadd bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
4584ad7e9b0SAdrian Chadd {
4594ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo;
4604ad7e9b0SAdrian Chadd 
4614ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
4624ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
4634ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
4644ad7e9b0SAdrian Chadd 		    type));
4654ad7e9b0SAdrian Chadd 
4664ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
4674ad7e9b0SAdrian Chadd 	switch (type) {
4684ad7e9b0SAdrian Chadd 	case BHND_PORT_DEVICE:
4694ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_dev_ports);
4704ad7e9b0SAdrian Chadd 	case BHND_PORT_BRIDGE:
4714ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_bridge_ports);
4724ad7e9b0SAdrian Chadd 	case BHND_PORT_AGENT:
4734ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_wrapper_ports);
474988fa8d0SAdrian Chadd 	default:
475988fa8d0SAdrian Chadd 		device_printf(dev, "%s: unknown type (%d)\n",
476988fa8d0SAdrian Chadd 		    __func__,
477988fa8d0SAdrian Chadd 		    type);
478988fa8d0SAdrian Chadd 		return (0);
4794ad7e9b0SAdrian Chadd 	}
4804ad7e9b0SAdrian Chadd }
4814ad7e9b0SAdrian Chadd 
4824ad7e9b0SAdrian Chadd static u_int
bcma_get_region_count(device_t dev,device_t child,bhnd_port_type type,u_int port_num)4834ad7e9b0SAdrian Chadd bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
4844ad7e9b0SAdrian Chadd     u_int port_num)
4854ad7e9b0SAdrian Chadd {
4864ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
4874ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
4884ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
4894ad7e9b0SAdrian Chadd 
4904ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
4914ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
4924ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
4934ad7e9b0SAdrian Chadd 		    type, port_num));
4944ad7e9b0SAdrian Chadd 
4954ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
4964ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
4974ad7e9b0SAdrian Chadd 
4984ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
4994ad7e9b0SAdrian Chadd 		if (port->sp_num == port_num)
5004ad7e9b0SAdrian Chadd 			return (port->sp_num_maps);
5014ad7e9b0SAdrian Chadd 	}
5024ad7e9b0SAdrian Chadd 
5034ad7e9b0SAdrian Chadd 	/* not found */
5044ad7e9b0SAdrian Chadd 	return (0);
5054ad7e9b0SAdrian Chadd }
5064ad7e9b0SAdrian Chadd 
5074ad7e9b0SAdrian Chadd static int
bcma_get_port_rid(device_t dev,device_t child,bhnd_port_type port_type,u_int port_num,u_int region_num)5084ad7e9b0SAdrian Chadd bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
5094ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num)
5104ad7e9b0SAdrian Chadd {
5114ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5124ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5134ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5144ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5154ad7e9b0SAdrian Chadd 
5164ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5174ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
5184ad7e9b0SAdrian Chadd 
5194ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
5204ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
5214ad7e9b0SAdrian Chadd 			continue;
5224ad7e9b0SAdrian Chadd 
5234ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
5244ad7e9b0SAdrian Chadd 			if (map->m_region_num == region_num)
5254ad7e9b0SAdrian Chadd 				return map->m_rid;
5264ad7e9b0SAdrian Chadd 	}
5274ad7e9b0SAdrian Chadd 
5284ad7e9b0SAdrian Chadd 	return -1;
5294ad7e9b0SAdrian Chadd }
5304ad7e9b0SAdrian Chadd 
5314ad7e9b0SAdrian Chadd static int
bcma_decode_port_rid(device_t dev,device_t child,int type,int rid,bhnd_port_type * port_type,u_int * port_num,u_int * region_num)5324ad7e9b0SAdrian Chadd bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
5334ad7e9b0SAdrian Chadd     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
5344ad7e9b0SAdrian Chadd {
5354ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5364ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5374ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5384ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5394ad7e9b0SAdrian Chadd 
5404ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5414ad7e9b0SAdrian Chadd 
5424ad7e9b0SAdrian Chadd 	/* Ports are always memory mapped */
5434ad7e9b0SAdrian Chadd 	if (type != SYS_RES_MEMORY)
5444ad7e9b0SAdrian Chadd 		return (EINVAL);
5454ad7e9b0SAdrian Chadd 
5464ad7e9b0SAdrian Chadd 	/* Starting with the most likely device list, search all three port
5474ad7e9b0SAdrian Chadd 	 * lists */
5484ad7e9b0SAdrian Chadd 	bhnd_port_type types[] = {
5494ad7e9b0SAdrian Chadd 	    BHND_PORT_DEVICE,
5504ad7e9b0SAdrian Chadd 	    BHND_PORT_AGENT,
5514ad7e9b0SAdrian Chadd 	    BHND_PORT_BRIDGE
5524ad7e9b0SAdrian Chadd 	};
5534ad7e9b0SAdrian Chadd 
5544ad7e9b0SAdrian Chadd 	for (int i = 0; i < nitems(types); i++) {
5554ad7e9b0SAdrian Chadd 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
5564ad7e9b0SAdrian Chadd 
5574ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(port, ports, sp_link) {
5584ad7e9b0SAdrian Chadd 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
5594ad7e9b0SAdrian Chadd 				if (map->m_rid != rid)
5604ad7e9b0SAdrian Chadd 					continue;
5614ad7e9b0SAdrian Chadd 
5624ad7e9b0SAdrian Chadd 				*port_type = port->sp_type;
5634ad7e9b0SAdrian Chadd 				*port_num = port->sp_num;
5644ad7e9b0SAdrian Chadd 				*region_num = map->m_region_num;
5654ad7e9b0SAdrian Chadd 				return (0);
5664ad7e9b0SAdrian Chadd 			}
5674ad7e9b0SAdrian Chadd 		}
5684ad7e9b0SAdrian Chadd 	}
5694ad7e9b0SAdrian Chadd 
5704ad7e9b0SAdrian Chadd 	return (ENOENT);
5714ad7e9b0SAdrian Chadd }
5724ad7e9b0SAdrian Chadd 
5734ad7e9b0SAdrian Chadd static int
bcma_get_region_addr(device_t dev,device_t child,bhnd_port_type port_type,u_int port_num,u_int region_num,bhnd_addr_t * addr,bhnd_size_t * size)5744ad7e9b0SAdrian Chadd bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
5754ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
5764ad7e9b0SAdrian Chadd {
5774ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
5784ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
5794ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
5804ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
5814ad7e9b0SAdrian Chadd 
5824ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
5834ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
5844ad7e9b0SAdrian Chadd 
5854ad7e9b0SAdrian Chadd 	/* Search the port list */
5864ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
5874ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
5884ad7e9b0SAdrian Chadd 			continue;
5894ad7e9b0SAdrian Chadd 
5904ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
5914ad7e9b0SAdrian Chadd 			if (map->m_region_num != region_num)
5924ad7e9b0SAdrian Chadd 				continue;
5934ad7e9b0SAdrian Chadd 
5944ad7e9b0SAdrian Chadd 			/* Found! */
5954ad7e9b0SAdrian Chadd 			*addr = map->m_base;
5964ad7e9b0SAdrian Chadd 			*size = map->m_size;
5974ad7e9b0SAdrian Chadd 			return (0);
5984ad7e9b0SAdrian Chadd 		}
5994ad7e9b0SAdrian Chadd 	}
6004ad7e9b0SAdrian Chadd 
6014ad7e9b0SAdrian Chadd 	return (ENOENT);
6024ad7e9b0SAdrian Chadd }
6034ad7e9b0SAdrian Chadd 
604824b48efSLandon J. Fuller /**
605824b48efSLandon J. Fuller  * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
606824b48efSLandon J. Fuller  */
607caeff9a3SLandon J. Fuller u_int
bcma_get_intr_count(device_t dev,device_t child)608824b48efSLandon J. Fuller bcma_get_intr_count(device_t dev, device_t child)
609824b48efSLandon J. Fuller {
610824b48efSLandon J. Fuller 	struct bcma_devinfo *dinfo;
611caeff9a3SLandon J. Fuller 
612caeff9a3SLandon J. Fuller 	/* delegate non-bus-attached devices to our parent */
613caeff9a3SLandon J. Fuller 	if (device_get_parent(child) != dev)
614caeff9a3SLandon J. Fuller 		return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
615824b48efSLandon J. Fuller 
616824b48efSLandon J. Fuller 	dinfo = device_get_ivars(child);
617caeff9a3SLandon J. Fuller 	return (dinfo->num_intrs);
618824b48efSLandon J. Fuller }
619824b48efSLandon J. Fuller 
620824b48efSLandon J. Fuller /**
621caeff9a3SLandon J. Fuller  * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
622824b48efSLandon J. Fuller  */
623824b48efSLandon J. Fuller int
bcma_get_intr_ivec(device_t dev,device_t child,u_int intr,u_int * ivec)624caeff9a3SLandon J. Fuller bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
625824b48efSLandon J. Fuller {
626824b48efSLandon J. Fuller 	struct bcma_devinfo	*dinfo;
627caeff9a3SLandon J. Fuller 	struct bcma_intr	*desc;
628caeff9a3SLandon J. Fuller 
629caeff9a3SLandon J. Fuller 	/* delegate non-bus-attached devices to our parent */
630caeff9a3SLandon J. Fuller 	if (device_get_parent(child) != dev) {
631caeff9a3SLandon J. Fuller 		return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
632caeff9a3SLandon J. Fuller 		    intr, ivec));
633caeff9a3SLandon J. Fuller 	}
634824b48efSLandon J. Fuller 
635824b48efSLandon J. Fuller 	dinfo = device_get_ivars(child);
636824b48efSLandon J. Fuller 
637caeff9a3SLandon J. Fuller 	STAILQ_FOREACH(desc, &dinfo->intrs, i_link) {
638caeff9a3SLandon J. Fuller 		if (desc->i_sel == intr) {
639caeff9a3SLandon J. Fuller 			*ivec = desc->i_busline;
640824b48efSLandon J. Fuller 			return (0);
641824b48efSLandon J. Fuller 		}
642caeff9a3SLandon J. Fuller 	}
643caeff9a3SLandon J. Fuller 
644caeff9a3SLandon J. Fuller 	/* Not found */
645caeff9a3SLandon J. Fuller 	return (ENXIO);
646caeff9a3SLandon J. Fuller }
647824b48efSLandon J. Fuller 
6484ad7e9b0SAdrian Chadd /**
649664a7497SLandon J. Fuller  * Scan the device enumeration ROM table, adding all valid discovered cores to
6504ad7e9b0SAdrian Chadd  * the bus.
6514ad7e9b0SAdrian Chadd  *
6524ad7e9b0SAdrian Chadd  * @param bus The bcma bus.
6534ad7e9b0SAdrian Chadd  */
6544ad7e9b0SAdrian Chadd int
bcma_add_children(device_t bus)655664a7497SLandon J. Fuller bcma_add_children(device_t bus)
6564ad7e9b0SAdrian Chadd {
657664a7497SLandon J. Fuller 	bhnd_erom_t			*erom;
658664a7497SLandon J. Fuller 	struct bcma_erom		*bcma_erom;
65989294a78SLandon J. Fuller 	struct bhnd_erom_io		*eio;
660664a7497SLandon J. Fuller 	const struct bhnd_chipid	*cid;
6614ad7e9b0SAdrian Chadd 	struct bcma_corecfg		*corecfg;
6624ad7e9b0SAdrian Chadd 	struct bcma_devinfo		*dinfo;
6634ad7e9b0SAdrian Chadd 	device_t			 child;
6644ad7e9b0SAdrian Chadd 	int				 error;
6654ad7e9b0SAdrian Chadd 
666664a7497SLandon J. Fuller 	cid = BHND_BUS_GET_CHIPID(bus, bus);
6674ad7e9b0SAdrian Chadd 	corecfg = NULL;
6684ad7e9b0SAdrian Chadd 
669664a7497SLandon J. Fuller 	/* Allocate our EROM parser */
67089294a78SLandon J. Fuller 	eio = bhnd_erom_iores_new(bus, BCMA_EROM_RID);
67189294a78SLandon J. Fuller 	erom = bhnd_erom_alloc(&bcma_erom_parser, cid, eio);
67289294a78SLandon J. Fuller 	if (erom == NULL) {
67389294a78SLandon J. Fuller 		bhnd_erom_io_fini(eio);
674664a7497SLandon J. Fuller 		return (ENODEV);
67589294a78SLandon J. Fuller 	}
6764ad7e9b0SAdrian Chadd 
6774ad7e9b0SAdrian Chadd 	/* Add all cores. */
678664a7497SLandon J. Fuller 	bcma_erom = (struct bcma_erom *)erom;
679664a7497SLandon J. Fuller 	while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
6804ad7e9b0SAdrian Chadd 		/* Add the child device */
681688fc8c0SLandon J. Fuller 		child = BUS_ADD_CHILD(bus, 0, NULL, -1);
6824ad7e9b0SAdrian Chadd 		if (child == NULL) {
6834ad7e9b0SAdrian Chadd 			error = ENXIO;
684111d7cb2SLandon J. Fuller 			goto cleanup;
6854ad7e9b0SAdrian Chadd 		}
6864ad7e9b0SAdrian Chadd 
687688fc8c0SLandon J. Fuller 		/* Initialize device ivars */
688688fc8c0SLandon J. Fuller 		dinfo = device_get_ivars(child);
689caeff9a3SLandon J. Fuller 		if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg)))
690111d7cb2SLandon J. Fuller 			goto cleanup;
691688fc8c0SLandon J. Fuller 
692688fc8c0SLandon J. Fuller 		/* The dinfo instance now owns the corecfg value */
693688fc8c0SLandon J. Fuller 		corecfg = NULL;
6944ad7e9b0SAdrian Chadd 
6954ad7e9b0SAdrian Chadd 		/* If pins are floating or the hardware is otherwise
6964ad7e9b0SAdrian Chadd 		 * unpopulated, the device shouldn't be used. */
6974ad7e9b0SAdrian Chadd 		if (bhnd_is_hw_disabled(child))
6984ad7e9b0SAdrian Chadd 			device_disable(child);
699f90f4b65SLandon J. Fuller 
700f90f4b65SLandon J. Fuller 		/* Issue bus callback for fully initialized child. */
701f90f4b65SLandon J. Fuller 		BHND_BUS_CHILD_ADDED(bus, child);
7024ad7e9b0SAdrian Chadd 	}
7034ad7e9b0SAdrian Chadd 
704111d7cb2SLandon J. Fuller 	/* EOF while parsing cores is expected */
7054ad7e9b0SAdrian Chadd 	if (error == ENOENT)
706664a7497SLandon J. Fuller 		error = 0;
7074ad7e9b0SAdrian Chadd 
708111d7cb2SLandon J. Fuller cleanup:
709664a7497SLandon J. Fuller 	bhnd_erom_free(erom);
710664a7497SLandon J. Fuller 
7114ad7e9b0SAdrian Chadd 	if (corecfg != NULL)
7124ad7e9b0SAdrian Chadd 		bcma_free_corecfg(corecfg);
7134ad7e9b0SAdrian Chadd 
714111d7cb2SLandon J. Fuller 	if (error)
715111d7cb2SLandon J. Fuller 		device_delete_children(bus);
716111d7cb2SLandon J. Fuller 
7174ad7e9b0SAdrian Chadd 	return (error);
7184ad7e9b0SAdrian Chadd }
7194ad7e9b0SAdrian Chadd 
7204ad7e9b0SAdrian Chadd static device_method_t bcma_methods[] = {
7214ad7e9b0SAdrian Chadd 	/* Device interface */
7224ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bcma_probe),
7234ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bcma_attach),
7244ad7e9b0SAdrian Chadd 	DEVMETHOD(device_detach,		bcma_detach),
7254ad7e9b0SAdrian Chadd 
7264ad7e9b0SAdrian Chadd 	/* Bus interface */
7278a03f98aSLandon J. Fuller 	DEVMETHOD(bus_add_child,		bcma_add_child),
7288a03f98aSLandon J. Fuller 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
7294ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
7304ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
7314ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
7324ad7e9b0SAdrian Chadd 
7334ad7e9b0SAdrian Chadd 	/* BHND interface */
734111d7cb2SLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_erom_class,	bcma_get_erom_class),
7358a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_ioctl,		bcma_read_ioctl),
7368a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_write_ioctl,		bcma_write_ioctl),
7378a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_iost,		bcma_read_iost),
7388a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_is_hw_suspended,	bcma_is_hw_suspended),
7398a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_reset_hw,		bcma_reset_hw),
7408a03f98aSLandon J. Fuller 	DEVMETHOD(bhnd_bus_suspend_hw,		bcma_suspend_hw),
741f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_read_config,		bcma_read_config),
742f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_write_config,	bcma_write_config),
7434ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
7444ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
7454ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
7464ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
7474ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
748824b48efSLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_intr_count,	bcma_get_intr_count),
749caeff9a3SLandon J. Fuller 	DEVMETHOD(bhnd_bus_get_intr_ivec,	bcma_get_intr_ivec),
7504ad7e9b0SAdrian Chadd 
7514ad7e9b0SAdrian Chadd 	DEVMETHOD_END
7524ad7e9b0SAdrian Chadd };
7534ad7e9b0SAdrian Chadd 
7544ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
7554ad7e9b0SAdrian Chadd MODULE_VERSION(bcma, 1);
7564ad7e9b0SAdrian Chadd MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
757