xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision 4ad7e9b0)
14ad7e9b0SAdrian Chadd /*-
24ad7e9b0SAdrian Chadd  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
34ad7e9b0SAdrian Chadd  * All rights reserved.
44ad7e9b0SAdrian Chadd  *
54ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
64ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
74ad7e9b0SAdrian Chadd  * are met:
84ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
94ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
104ad7e9b0SAdrian Chadd  *    without modification.
114ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
124ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
134ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
144ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
154ad7e9b0SAdrian Chadd  *
164ad7e9b0SAdrian Chadd  * NO WARRANTY
174ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
184ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
194ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
204ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
214ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
224ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
244ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
254ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
264ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
274ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
284ad7e9b0SAdrian Chadd  */
294ad7e9b0SAdrian Chadd 
304ad7e9b0SAdrian Chadd #include <sys/cdefs.h>
314ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$");
324ad7e9b0SAdrian Chadd 
334ad7e9b0SAdrian Chadd #include <sys/param.h>
344ad7e9b0SAdrian Chadd #include <sys/bus.h>
354ad7e9b0SAdrian Chadd #include <sys/kernel.h>
364ad7e9b0SAdrian Chadd #include <sys/malloc.h>
374ad7e9b0SAdrian Chadd #include <sys/module.h>
384ad7e9b0SAdrian Chadd #include <sys/systm.h>
394ad7e9b0SAdrian Chadd 
404ad7e9b0SAdrian Chadd #include <machine/bus.h>
414ad7e9b0SAdrian Chadd 
424ad7e9b0SAdrian Chadd #include "bcmavar.h"
434ad7e9b0SAdrian Chadd 
444ad7e9b0SAdrian Chadd #include "bcma_eromreg.h"
454ad7e9b0SAdrian Chadd #include "bcma_eromvar.h"
464ad7e9b0SAdrian Chadd 
474ad7e9b0SAdrian Chadd int
484ad7e9b0SAdrian Chadd bcma_probe(device_t dev)
494ad7e9b0SAdrian Chadd {
504ad7e9b0SAdrian Chadd 	device_set_desc(dev, "BCMA BHND bus");
514ad7e9b0SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
524ad7e9b0SAdrian Chadd }
534ad7e9b0SAdrian Chadd 
544ad7e9b0SAdrian Chadd int
554ad7e9b0SAdrian Chadd bcma_attach(device_t dev)
564ad7e9b0SAdrian Chadd {
574ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
584ad7e9b0SAdrian Chadd 	device_t		*devs, child;
594ad7e9b0SAdrian Chadd 	int			 ndevs;
604ad7e9b0SAdrian Chadd 	int			 error;
614ad7e9b0SAdrian Chadd 
624ad7e9b0SAdrian Chadd 
634ad7e9b0SAdrian Chadd 	if ((error = device_get_children(dev, &devs, &ndevs)))
644ad7e9b0SAdrian Chadd 		return (error);
654ad7e9b0SAdrian Chadd 
664ad7e9b0SAdrian Chadd 	/*
674ad7e9b0SAdrian Chadd 	 * Map our children's agent register block.
684ad7e9b0SAdrian Chadd 	 */
694ad7e9b0SAdrian Chadd 	for (int i = 0; i < ndevs; i++) {
704ad7e9b0SAdrian Chadd 		bhnd_addr_t	addr;
714ad7e9b0SAdrian Chadd 		bhnd_size_t	size;
724ad7e9b0SAdrian Chadd 		rman_res_t	r_start, r_count, r_end;
734ad7e9b0SAdrian Chadd 
744ad7e9b0SAdrian Chadd 		child = devs[i];
754ad7e9b0SAdrian Chadd 		dinfo = device_get_ivars(child);
764ad7e9b0SAdrian Chadd 
774ad7e9b0SAdrian Chadd 		KASSERT(!device_is_suspended(child),
784ad7e9b0SAdrian Chadd 		    ("bcma(4) stateful suspend handling requires that devices "
794ad7e9b0SAdrian Chadd 		        "not be suspended before bcma_attach()"));
804ad7e9b0SAdrian Chadd 
814ad7e9b0SAdrian Chadd 		/* Verify that the agent register block exists and is
824ad7e9b0SAdrian Chadd 		 * mappable */
834ad7e9b0SAdrian Chadd 		if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
844ad7e9b0SAdrian Chadd 			continue;
854ad7e9b0SAdrian Chadd 
864ad7e9b0SAdrian Chadd 		/* Fetch the address of the agent register block */
874ad7e9b0SAdrian Chadd 		error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
884ad7e9b0SAdrian Chadd 		    &addr, &size);
894ad7e9b0SAdrian Chadd 		if (error) {
904ad7e9b0SAdrian Chadd 			device_printf(dev, "failed fetching agent register "
914ad7e9b0SAdrian Chadd 			    "block address for core %d\n", i);
924ad7e9b0SAdrian Chadd 			goto cleanup;
934ad7e9b0SAdrian Chadd 		}
944ad7e9b0SAdrian Chadd 
954ad7e9b0SAdrian Chadd 		/* Allocate the resource */
964ad7e9b0SAdrian Chadd 		r_start = addr;
974ad7e9b0SAdrian Chadd 		r_count = size;
984ad7e9b0SAdrian Chadd 		r_end = r_start + r_count - 1;
994ad7e9b0SAdrian Chadd 
1004ad7e9b0SAdrian Chadd 		dinfo->rid_agent = 0;
1014ad7e9b0SAdrian Chadd 		dinfo->res_agent = bhnd_alloc_resource(dev, SYS_RES_MEMORY,
1024ad7e9b0SAdrian Chadd 		    &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
1034ad7e9b0SAdrian Chadd 		if (dinfo->res_agent == NULL) {
1044ad7e9b0SAdrian Chadd 			device_printf(dev, "failed allocating agent register "
1054ad7e9b0SAdrian Chadd 			    "block for core %d\n", i);
1064ad7e9b0SAdrian Chadd 			error = ENXIO;
1074ad7e9b0SAdrian Chadd 			goto cleanup;
1084ad7e9b0SAdrian Chadd 		}
1094ad7e9b0SAdrian Chadd 	}
1104ad7e9b0SAdrian Chadd 
1114ad7e9b0SAdrian Chadd cleanup:
1124ad7e9b0SAdrian Chadd 	free(devs, M_BHND);
1134ad7e9b0SAdrian Chadd 	if (error)
1144ad7e9b0SAdrian Chadd 		return (error);
1154ad7e9b0SAdrian Chadd 
1164ad7e9b0SAdrian Chadd 	return (bhnd_generic_attach(dev));
1174ad7e9b0SAdrian Chadd }
1184ad7e9b0SAdrian Chadd 
1194ad7e9b0SAdrian Chadd int
1204ad7e9b0SAdrian Chadd bcma_detach(device_t dev)
1214ad7e9b0SAdrian Chadd {
1224ad7e9b0SAdrian Chadd 	return (bhnd_generic_detach(dev));
1234ad7e9b0SAdrian Chadd }
1244ad7e9b0SAdrian Chadd 
1254ad7e9b0SAdrian Chadd static int
1264ad7e9b0SAdrian Chadd bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
1274ad7e9b0SAdrian Chadd {
1284ad7e9b0SAdrian Chadd 	const struct bcma_devinfo *dinfo;
1294ad7e9b0SAdrian Chadd 	const struct bhnd_core_info *ci;
1304ad7e9b0SAdrian Chadd 
1314ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
1324ad7e9b0SAdrian Chadd 	ci = &dinfo->corecfg->core_info;
1334ad7e9b0SAdrian Chadd 
1344ad7e9b0SAdrian Chadd 	switch (index) {
1354ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1364ad7e9b0SAdrian Chadd 		*result = ci->vendor;
1374ad7e9b0SAdrian Chadd 		return (0);
1384ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1394ad7e9b0SAdrian Chadd 		*result = ci->device;
1404ad7e9b0SAdrian Chadd 		return (0);
1414ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1424ad7e9b0SAdrian Chadd 		*result = ci->hwrev;
1434ad7e9b0SAdrian Chadd 		return (0);
1444ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1454ad7e9b0SAdrian Chadd 		*result = bhnd_core_class(ci);
1464ad7e9b0SAdrian Chadd 		return (0);
1474ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1484ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
1494ad7e9b0SAdrian Chadd 		return (0);
1504ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1514ad7e9b0SAdrian Chadd 		*result = (uintptr_t) bhnd_core_name(ci);
1524ad7e9b0SAdrian Chadd 		return (0);
1534ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1544ad7e9b0SAdrian Chadd 		*result = ci->core_idx;
1554ad7e9b0SAdrian Chadd 		return (0);
1564ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1574ad7e9b0SAdrian Chadd 		*result = ci->unit;
1584ad7e9b0SAdrian Chadd 		return (0);
1594ad7e9b0SAdrian Chadd 	default:
1604ad7e9b0SAdrian Chadd 		return (ENOENT);
1614ad7e9b0SAdrian Chadd 	}
1624ad7e9b0SAdrian Chadd }
1634ad7e9b0SAdrian Chadd 
1644ad7e9b0SAdrian Chadd static int
1654ad7e9b0SAdrian Chadd bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
1664ad7e9b0SAdrian Chadd {
1674ad7e9b0SAdrian Chadd 	switch (index) {
1684ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR:
1694ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE:
1704ad7e9b0SAdrian Chadd 	case BHND_IVAR_HWREV:
1714ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_CLASS:
1724ad7e9b0SAdrian Chadd 	case BHND_IVAR_VENDOR_NAME:
1734ad7e9b0SAdrian Chadd 	case BHND_IVAR_DEVICE_NAME:
1744ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_INDEX:
1754ad7e9b0SAdrian Chadd 	case BHND_IVAR_CORE_UNIT:
1764ad7e9b0SAdrian Chadd 		return (EINVAL);
1774ad7e9b0SAdrian Chadd 	default:
1784ad7e9b0SAdrian Chadd 		return (ENOENT);
1794ad7e9b0SAdrian Chadd 	}
1804ad7e9b0SAdrian Chadd }
1814ad7e9b0SAdrian Chadd 
1824ad7e9b0SAdrian Chadd static void
1834ad7e9b0SAdrian Chadd bcma_child_deleted(device_t dev, device_t child)
1844ad7e9b0SAdrian Chadd {
1854ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo = device_get_ivars(child);
1864ad7e9b0SAdrian Chadd 	if (dinfo != NULL)
1874ad7e9b0SAdrian Chadd 		bcma_free_dinfo(dev, dinfo);
1884ad7e9b0SAdrian Chadd }
1894ad7e9b0SAdrian Chadd 
1904ad7e9b0SAdrian Chadd static struct resource_list *
1914ad7e9b0SAdrian Chadd bcma_get_resource_list(device_t dev, device_t child)
1924ad7e9b0SAdrian Chadd {
1934ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo = device_get_ivars(child);
1944ad7e9b0SAdrian Chadd 	return (&dinfo->resources);
1954ad7e9b0SAdrian Chadd }
1964ad7e9b0SAdrian Chadd 
1974ad7e9b0SAdrian Chadd 
1984ad7e9b0SAdrian Chadd static int
1994ad7e9b0SAdrian Chadd bcma_reset_core(device_t dev, device_t child, uint16_t flags)
2004ad7e9b0SAdrian Chadd {
2014ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo;
2024ad7e9b0SAdrian Chadd 
2034ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2044ad7e9b0SAdrian Chadd 		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
2054ad7e9b0SAdrian Chadd 
2064ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2074ad7e9b0SAdrian Chadd 
2084ad7e9b0SAdrian Chadd 	/* Can't reset the core without access to the agent registers */
2094ad7e9b0SAdrian Chadd 	if (dinfo->res_agent == NULL)
2104ad7e9b0SAdrian Chadd 		return (ENODEV);
2114ad7e9b0SAdrian Chadd 
2124ad7e9b0SAdrian Chadd 	// TODO - perform reset
2134ad7e9b0SAdrian Chadd 
2144ad7e9b0SAdrian Chadd 	return (ENXIO);
2154ad7e9b0SAdrian Chadd }
2164ad7e9b0SAdrian Chadd 
2174ad7e9b0SAdrian Chadd static int
2184ad7e9b0SAdrian Chadd bcma_suspend_core(device_t dev, device_t child)
2194ad7e9b0SAdrian Chadd {
2204ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo;
2214ad7e9b0SAdrian Chadd 
2224ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2234ad7e9b0SAdrian Chadd 		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
2244ad7e9b0SAdrian Chadd 
2254ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2264ad7e9b0SAdrian Chadd 
2274ad7e9b0SAdrian Chadd 	/* Can't suspend the core without access to the agent registers */
2284ad7e9b0SAdrian Chadd 	if (dinfo->res_agent == NULL)
2294ad7e9b0SAdrian Chadd 		return (ENODEV);
2304ad7e9b0SAdrian Chadd 
2314ad7e9b0SAdrian Chadd 	// TODO - perform suspend
2324ad7e9b0SAdrian Chadd 
2334ad7e9b0SAdrian Chadd 	return (ENXIO);
2344ad7e9b0SAdrian Chadd }
2354ad7e9b0SAdrian Chadd 
2364ad7e9b0SAdrian Chadd static u_int
2374ad7e9b0SAdrian Chadd bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
2384ad7e9b0SAdrian Chadd {
2394ad7e9b0SAdrian Chadd 	struct bcma_devinfo *dinfo;
2404ad7e9b0SAdrian Chadd 
2414ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
2424ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2434ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
2444ad7e9b0SAdrian Chadd 		    type));
2454ad7e9b0SAdrian Chadd 
2464ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2474ad7e9b0SAdrian Chadd 	switch (type) {
2484ad7e9b0SAdrian Chadd 	case BHND_PORT_DEVICE:
2494ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_dev_ports);
2504ad7e9b0SAdrian Chadd 	case BHND_PORT_BRIDGE:
2514ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_bridge_ports);
2524ad7e9b0SAdrian Chadd 	case BHND_PORT_AGENT:
2534ad7e9b0SAdrian Chadd 		return (dinfo->corecfg->num_wrapper_ports);
2544ad7e9b0SAdrian Chadd 	}
2554ad7e9b0SAdrian Chadd }
2564ad7e9b0SAdrian Chadd 
2574ad7e9b0SAdrian Chadd static u_int
2584ad7e9b0SAdrian Chadd bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
2594ad7e9b0SAdrian Chadd     u_int port_num)
2604ad7e9b0SAdrian Chadd {
2614ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2624ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
2634ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
2644ad7e9b0SAdrian Chadd 
2654ad7e9b0SAdrian Chadd 	/* delegate non-bus-attached devices to our parent */
2664ad7e9b0SAdrian Chadd 	if (device_get_parent(child) != dev)
2674ad7e9b0SAdrian Chadd 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
2684ad7e9b0SAdrian Chadd 		    type, port_num));
2694ad7e9b0SAdrian Chadd 
2704ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2714ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
2724ad7e9b0SAdrian Chadd 
2734ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
2744ad7e9b0SAdrian Chadd 		if (port->sp_num == port_num)
2754ad7e9b0SAdrian Chadd 			return (port->sp_num_maps);
2764ad7e9b0SAdrian Chadd 	}
2774ad7e9b0SAdrian Chadd 
2784ad7e9b0SAdrian Chadd 	/* not found */
2794ad7e9b0SAdrian Chadd 	return (0);
2804ad7e9b0SAdrian Chadd }
2814ad7e9b0SAdrian Chadd 
2824ad7e9b0SAdrian Chadd static int
2834ad7e9b0SAdrian Chadd bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
2844ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num)
2854ad7e9b0SAdrian Chadd {
2864ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
2874ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
2884ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
2894ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
2904ad7e9b0SAdrian Chadd 
2914ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
2924ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
2934ad7e9b0SAdrian Chadd 
2944ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
2954ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
2964ad7e9b0SAdrian Chadd 			continue;
2974ad7e9b0SAdrian Chadd 
2984ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
2994ad7e9b0SAdrian Chadd 			if (map->m_region_num == region_num)
3004ad7e9b0SAdrian Chadd 				return map->m_rid;
3014ad7e9b0SAdrian Chadd 	}
3024ad7e9b0SAdrian Chadd 
3034ad7e9b0SAdrian Chadd 	return -1;
3044ad7e9b0SAdrian Chadd }
3054ad7e9b0SAdrian Chadd 
3064ad7e9b0SAdrian Chadd static int
3074ad7e9b0SAdrian Chadd bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
3084ad7e9b0SAdrian Chadd     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
3094ad7e9b0SAdrian Chadd {
3104ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
3114ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
3124ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
3134ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
3144ad7e9b0SAdrian Chadd 
3154ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
3164ad7e9b0SAdrian Chadd 
3174ad7e9b0SAdrian Chadd 	/* Ports are always memory mapped */
3184ad7e9b0SAdrian Chadd 	if (type != SYS_RES_MEMORY)
3194ad7e9b0SAdrian Chadd 		return (EINVAL);
3204ad7e9b0SAdrian Chadd 
3214ad7e9b0SAdrian Chadd 	/* Starting with the most likely device list, search all three port
3224ad7e9b0SAdrian Chadd 	 * lists */
3234ad7e9b0SAdrian Chadd 	bhnd_port_type types[] = {
3244ad7e9b0SAdrian Chadd 	    BHND_PORT_DEVICE,
3254ad7e9b0SAdrian Chadd 	    BHND_PORT_AGENT,
3264ad7e9b0SAdrian Chadd 	    BHND_PORT_BRIDGE
3274ad7e9b0SAdrian Chadd 	};
3284ad7e9b0SAdrian Chadd 
3294ad7e9b0SAdrian Chadd 	for (int i = 0; i < nitems(types); i++) {
3304ad7e9b0SAdrian Chadd 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
3314ad7e9b0SAdrian Chadd 
3324ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(port, ports, sp_link) {
3334ad7e9b0SAdrian Chadd 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
3344ad7e9b0SAdrian Chadd 				if (map->m_rid != rid)
3354ad7e9b0SAdrian Chadd 					continue;
3364ad7e9b0SAdrian Chadd 
3374ad7e9b0SAdrian Chadd 				*port_type = port->sp_type;
3384ad7e9b0SAdrian Chadd 				*port_num = port->sp_num;
3394ad7e9b0SAdrian Chadd 				*region_num = map->m_region_num;
3404ad7e9b0SAdrian Chadd 				return (0);
3414ad7e9b0SAdrian Chadd 			}
3424ad7e9b0SAdrian Chadd 		}
3434ad7e9b0SAdrian Chadd 	}
3444ad7e9b0SAdrian Chadd 
3454ad7e9b0SAdrian Chadd 	return (ENOENT);
3464ad7e9b0SAdrian Chadd }
3474ad7e9b0SAdrian Chadd 
3484ad7e9b0SAdrian Chadd static int
3494ad7e9b0SAdrian Chadd bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
3504ad7e9b0SAdrian Chadd     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
3514ad7e9b0SAdrian Chadd {
3524ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
3534ad7e9b0SAdrian Chadd 	struct bcma_map		*map;
3544ad7e9b0SAdrian Chadd 	struct bcma_sport_list	*ports;
3554ad7e9b0SAdrian Chadd 	struct bcma_sport	*port;
3564ad7e9b0SAdrian Chadd 
3574ad7e9b0SAdrian Chadd 	dinfo = device_get_ivars(child);
3584ad7e9b0SAdrian Chadd 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
3594ad7e9b0SAdrian Chadd 
3604ad7e9b0SAdrian Chadd 	/* Search the port list */
3614ad7e9b0SAdrian Chadd 	STAILQ_FOREACH(port, ports, sp_link) {
3624ad7e9b0SAdrian Chadd 		if (port->sp_num != port_num)
3634ad7e9b0SAdrian Chadd 			continue;
3644ad7e9b0SAdrian Chadd 
3654ad7e9b0SAdrian Chadd 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
3664ad7e9b0SAdrian Chadd 			if (map->m_region_num != region_num)
3674ad7e9b0SAdrian Chadd 				continue;
3684ad7e9b0SAdrian Chadd 
3694ad7e9b0SAdrian Chadd 			/* Found! */
3704ad7e9b0SAdrian Chadd 			*addr = map->m_base;
3714ad7e9b0SAdrian Chadd 			*size = map->m_size;
3724ad7e9b0SAdrian Chadd 			return (0);
3734ad7e9b0SAdrian Chadd 		}
3744ad7e9b0SAdrian Chadd 	}
3754ad7e9b0SAdrian Chadd 
3764ad7e9b0SAdrian Chadd 	return (ENOENT);
3774ad7e9b0SAdrian Chadd }
3784ad7e9b0SAdrian Chadd 
3794ad7e9b0SAdrian Chadd /**
3804ad7e9b0SAdrian Chadd  * Scan a device enumeration ROM table, adding all valid discovered cores to
3814ad7e9b0SAdrian Chadd  * the bus.
3824ad7e9b0SAdrian Chadd  *
3834ad7e9b0SAdrian Chadd  * @param bus The bcma bus.
3844ad7e9b0SAdrian Chadd  * @param erom_res An active resource mapping the EROM core.
3854ad7e9b0SAdrian Chadd  * @param erom_offset Base offset of the EROM core's register mapping.
3864ad7e9b0SAdrian Chadd  */
3874ad7e9b0SAdrian Chadd int
3884ad7e9b0SAdrian Chadd bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
3894ad7e9b0SAdrian Chadd {
3904ad7e9b0SAdrian Chadd 	struct bcma_erom	 erom;
3914ad7e9b0SAdrian Chadd 	struct bcma_corecfg	*corecfg;
3924ad7e9b0SAdrian Chadd 	struct bcma_devinfo	*dinfo;
3934ad7e9b0SAdrian Chadd 	device_t		 child;
3944ad7e9b0SAdrian Chadd 	int			 error;
3954ad7e9b0SAdrian Chadd 
3964ad7e9b0SAdrian Chadd 	dinfo = NULL;
3974ad7e9b0SAdrian Chadd 	corecfg = NULL;
3984ad7e9b0SAdrian Chadd 
3994ad7e9b0SAdrian Chadd 	/* Initialize our reader */
4004ad7e9b0SAdrian Chadd 	error = bcma_erom_open(&erom, erom_res, erom_offset);
4014ad7e9b0SAdrian Chadd 	if (error)
4024ad7e9b0SAdrian Chadd 		return (error);
4034ad7e9b0SAdrian Chadd 
4044ad7e9b0SAdrian Chadd 	/* Add all cores. */
4054ad7e9b0SAdrian Chadd 	while (!error) {
4064ad7e9b0SAdrian Chadd 		/* Parse next core */
4074ad7e9b0SAdrian Chadd 		error = bcma_erom_parse_corecfg(&erom, &corecfg);
4084ad7e9b0SAdrian Chadd 		if (error && error == ENOENT) {
4094ad7e9b0SAdrian Chadd 			return (0);
4104ad7e9b0SAdrian Chadd 		} else if (error) {
4114ad7e9b0SAdrian Chadd 			goto failed;
4124ad7e9b0SAdrian Chadd 		}
4134ad7e9b0SAdrian Chadd 
4144ad7e9b0SAdrian Chadd 		/* Allocate per-device bus info */
4154ad7e9b0SAdrian Chadd 		dinfo = bcma_alloc_dinfo(bus, corecfg);
4164ad7e9b0SAdrian Chadd 		if (dinfo == NULL) {
4174ad7e9b0SAdrian Chadd 			error = ENXIO;
4184ad7e9b0SAdrian Chadd 			goto failed;
4194ad7e9b0SAdrian Chadd 		}
4204ad7e9b0SAdrian Chadd 
4214ad7e9b0SAdrian Chadd 		/* The dinfo instance now owns the corecfg value */
4224ad7e9b0SAdrian Chadd 		corecfg = NULL;
4234ad7e9b0SAdrian Chadd 
4244ad7e9b0SAdrian Chadd 		/* Add the child device */
4254ad7e9b0SAdrian Chadd 		child = device_add_child(bus, NULL, -1);
4264ad7e9b0SAdrian Chadd 		if (child == NULL) {
4274ad7e9b0SAdrian Chadd 			error = ENXIO;
4284ad7e9b0SAdrian Chadd 			goto failed;
4294ad7e9b0SAdrian Chadd 		}
4304ad7e9b0SAdrian Chadd 
4314ad7e9b0SAdrian Chadd 		/* The child device now owns the dinfo pointer */
4324ad7e9b0SAdrian Chadd 		device_set_ivars(child, dinfo);
4334ad7e9b0SAdrian Chadd 		dinfo = NULL;
4344ad7e9b0SAdrian Chadd 
4354ad7e9b0SAdrian Chadd 		/* If pins are floating or the hardware is otherwise
4364ad7e9b0SAdrian Chadd 		 * unpopulated, the device shouldn't be used. */
4374ad7e9b0SAdrian Chadd 		if (bhnd_is_hw_disabled(child))
4384ad7e9b0SAdrian Chadd 			device_disable(child);
4394ad7e9b0SAdrian Chadd 	}
4404ad7e9b0SAdrian Chadd 
4414ad7e9b0SAdrian Chadd 	/* Hit EOF parsing cores? */
4424ad7e9b0SAdrian Chadd 	if (error == ENOENT)
4434ad7e9b0SAdrian Chadd 		return (0);
4444ad7e9b0SAdrian Chadd 
4454ad7e9b0SAdrian Chadd failed:
4464ad7e9b0SAdrian Chadd 	if (dinfo != NULL)
4474ad7e9b0SAdrian Chadd 		bcma_free_dinfo(bus, dinfo);
4484ad7e9b0SAdrian Chadd 
4494ad7e9b0SAdrian Chadd 	if (corecfg != NULL)
4504ad7e9b0SAdrian Chadd 		bcma_free_corecfg(corecfg);
4514ad7e9b0SAdrian Chadd 
4524ad7e9b0SAdrian Chadd 	return (error);
4534ad7e9b0SAdrian Chadd }
4544ad7e9b0SAdrian Chadd 
4554ad7e9b0SAdrian Chadd 
4564ad7e9b0SAdrian Chadd static device_method_t bcma_methods[] = {
4574ad7e9b0SAdrian Chadd 	/* Device interface */
4584ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bcma_probe),
4594ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bcma_attach),
4604ad7e9b0SAdrian Chadd 	DEVMETHOD(device_detach,		bcma_detach),
4614ad7e9b0SAdrian Chadd 
4624ad7e9b0SAdrian Chadd 	/* Bus interface */
4634ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
4644ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
4654ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
4664ad7e9b0SAdrian Chadd 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
4674ad7e9b0SAdrian Chadd 
4684ad7e9b0SAdrian Chadd 	/* BHND interface */
4694ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
4704ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
4714ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
4724ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
4734ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
4744ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
4754ad7e9b0SAdrian Chadd 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
4764ad7e9b0SAdrian Chadd 
4774ad7e9b0SAdrian Chadd 	DEVMETHOD_END
4784ad7e9b0SAdrian Chadd };
4794ad7e9b0SAdrian Chadd 
4804ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
4814ad7e9b0SAdrian Chadd MODULE_VERSION(bcma, 1);
4824ad7e9b0SAdrian Chadd MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
483