xref: /freebsd/sys/dev/bhnd/siba/siba_subr.c (revision 7c7c726bca778fa6f49c9ed6ce681c3b281bcdd8)
14ad7e9b0SAdrian Chadd /*-
2caeff9a3SLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
3caeff9a3SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
44ad7e9b0SAdrian Chadd  * All rights reserved.
54ad7e9b0SAdrian Chadd  *
6caeff9a3SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
7caeff9a3SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
8caeff9a3SLandon J. Fuller  *
94ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
104ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
114ad7e9b0SAdrian Chadd  * are met:
124ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
134ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
144ad7e9b0SAdrian Chadd  *    without modification.
154ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
164ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
174ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
184ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
194ad7e9b0SAdrian Chadd  *
204ad7e9b0SAdrian Chadd  * NO WARRANTY
214ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
224ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
234ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
244ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
254ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
264ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
274ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
284ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
294ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
304ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
314ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
324ad7e9b0SAdrian Chadd  */
334ad7e9b0SAdrian Chadd 
344ad7e9b0SAdrian Chadd #include <sys/cdefs.h>
354ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$");
364ad7e9b0SAdrian Chadd 
374ad7e9b0SAdrian Chadd #include <sys/param.h>
384ad7e9b0SAdrian Chadd #include <sys/bus.h>
394ad7e9b0SAdrian Chadd #include <sys/kernel.h>
404ad7e9b0SAdrian Chadd #include <sys/limits.h>
414ad7e9b0SAdrian Chadd #include <sys/systm.h>
424ad7e9b0SAdrian Chadd 
434ad7e9b0SAdrian Chadd #include <machine/bus.h>
444ad7e9b0SAdrian Chadd #include <machine/resource.h>
454ad7e9b0SAdrian Chadd 
464ad7e9b0SAdrian Chadd #include <dev/bhnd/bhndvar.h>
474ad7e9b0SAdrian Chadd 
484ad7e9b0SAdrian Chadd #include "sibareg.h"
494ad7e9b0SAdrian Chadd #include "sibavar.h"
504ad7e9b0SAdrian Chadd 
51*7c7c726bSLandon J. Fuller static int	siba_register_interrupts(device_t dev, device_t child,
52*7c7c726bSLandon J. Fuller 		    struct siba_devinfo *dinfo);
53*7c7c726bSLandon J. Fuller static int	siba_append_dinfo_region(struct siba_devinfo *dinfo,
54*7c7c726bSLandon J. Fuller 		     uint8_t addridx, uint32_t base, uint32_t size,
55*7c7c726bSLandon J. Fuller 		     uint32_t bus_reserved);
56*7c7c726bSLandon J. Fuller 
574ad7e9b0SAdrian Chadd /**
584ad7e9b0SAdrian Chadd  * Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor
594ad7e9b0SAdrian Chadd  * code.
604ad7e9b0SAdrian Chadd  *
614ad7e9b0SAdrian Chadd  * @param ocp_vendor An OCP vendor code.
624ad7e9b0SAdrian Chadd  * @return The BHND_MFGID constant corresponding to @p ocp_vendor, or
634ad7e9b0SAdrian Chadd  * BHND_MFGID_INVALID if the OCP vendor is unknown.
644ad7e9b0SAdrian Chadd  */
654ad7e9b0SAdrian Chadd uint16_t
664ad7e9b0SAdrian Chadd siba_get_bhnd_mfgid(uint16_t ocp_vendor)
674ad7e9b0SAdrian Chadd {
684ad7e9b0SAdrian Chadd 	switch (ocp_vendor) {
694ad7e9b0SAdrian Chadd 	case OCP_VENDOR_BCM:
704ad7e9b0SAdrian Chadd 		return (BHND_MFGID_BCM);
714ad7e9b0SAdrian Chadd 	default:
724ad7e9b0SAdrian Chadd 		return (BHND_MFGID_INVALID);
734ad7e9b0SAdrian Chadd 	}
744ad7e9b0SAdrian Chadd }
754ad7e9b0SAdrian Chadd 
764ad7e9b0SAdrian Chadd /**
77688fc8c0SLandon J. Fuller  * Allocate and return a new empty device info structure.
784ad7e9b0SAdrian Chadd  *
79688fc8c0SLandon J. Fuller  * @param bus The requesting bus device.
80688fc8c0SLandon J. Fuller  *
81688fc8c0SLandon J. Fuller  * @retval NULL if allocation failed.
824ad7e9b0SAdrian Chadd  */
834ad7e9b0SAdrian Chadd struct siba_devinfo *
84688fc8c0SLandon J. Fuller siba_alloc_dinfo(device_t bus)
854ad7e9b0SAdrian Chadd {
864ad7e9b0SAdrian Chadd 	struct siba_devinfo *dinfo;
874ad7e9b0SAdrian Chadd 
88688fc8c0SLandon J. Fuller 	dinfo = malloc(sizeof(struct siba_devinfo), M_BHND, M_NOWAIT|M_ZERO);
894ad7e9b0SAdrian Chadd 	if (dinfo == NULL)
904ad7e9b0SAdrian Chadd 		return NULL;
914ad7e9b0SAdrian Chadd 
924ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
93caeff9a3SLandon J. Fuller 		dinfo->cfg[i] = ((struct siba_cfg_block){
94caeff9a3SLandon J. Fuller 			.cb_base = 0,
95caeff9a3SLandon J. Fuller 			.cb_size = 0,
96caeff9a3SLandon J. Fuller 			.cb_rid = -1,
97caeff9a3SLandon J. Fuller 		});
98caeff9a3SLandon J. Fuller 		dinfo->cfg_res[i] = NULL;
994ad7e9b0SAdrian Chadd 		dinfo->cfg_rid[i] = -1;
1004ad7e9b0SAdrian Chadd 	}
1014ad7e9b0SAdrian Chadd 
1024ad7e9b0SAdrian Chadd 	resource_list_init(&dinfo->resources);
1034ad7e9b0SAdrian Chadd 
1044e96bf3aSLandon J. Fuller 	dinfo->pmu_state = SIBA_PMU_NONE;
105*7c7c726bSLandon J. Fuller 
106*7c7c726bSLandon J. Fuller 	dinfo->intr = (struct siba_intr) {
107*7c7c726bSLandon J. Fuller 		.mapped = false,
108*7c7c726bSLandon J. Fuller 		.rid = -1
109*7c7c726bSLandon J. Fuller 	};
110caeff9a3SLandon J. Fuller 
1114ad7e9b0SAdrian Chadd 	return dinfo;
1124ad7e9b0SAdrian Chadd }
1134ad7e9b0SAdrian Chadd 
1144ad7e9b0SAdrian Chadd /**
115688fc8c0SLandon J. Fuller  * Initialize a device info structure previously allocated via
116688fc8c0SLandon J. Fuller  * siba_alloc_dinfo, copying the provided core id.
117688fc8c0SLandon J. Fuller  *
118688fc8c0SLandon J. Fuller  * @param dev The requesting bus device.
119*7c7c726bSLandon J. Fuller  * @param child The siba child device.
120688fc8c0SLandon J. Fuller  * @param dinfo The device info instance.
121688fc8c0SLandon J. Fuller  * @param core Device core info.
122688fc8c0SLandon J. Fuller  *
123688fc8c0SLandon J. Fuller  * @retval 0 success
124688fc8c0SLandon J. Fuller  * @retval non-zero initialization failed.
125688fc8c0SLandon J. Fuller  */
126688fc8c0SLandon J. Fuller int
127*7c7c726bSLandon J. Fuller siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,
128688fc8c0SLandon J. Fuller     const struct siba_core_id *core_id)
129688fc8c0SLandon J. Fuller {
130*7c7c726bSLandon J. Fuller 	int error;
131*7c7c726bSLandon J. Fuller 
132688fc8c0SLandon J. Fuller 	dinfo->core_id = *core_id;
133*7c7c726bSLandon J. Fuller 
134*7c7c726bSLandon J. Fuller 	/* Register all address space mappings */
135*7c7c726bSLandon J. Fuller 	for (uint8_t i = 0; i < core_id->num_admatch; i++) {
136*7c7c726bSLandon J. Fuller 		uint32_t bus_reserved;
137*7c7c726bSLandon J. Fuller 
138*7c7c726bSLandon J. Fuller 		/* If this is the device's core/enumeration addrespace,
139*7c7c726bSLandon J. Fuller 		 * reserve the Sonics configuration register blocks for the
140*7c7c726bSLandon J. Fuller 		 * use of our bus. */
141*7c7c726bSLandon J. Fuller 		bus_reserved = 0;
142*7c7c726bSLandon J. Fuller 		if (i == SIBA_CORE_ADDRSPACE)
143*7c7c726bSLandon J. Fuller 			bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;
144*7c7c726bSLandon J. Fuller 
145*7c7c726bSLandon J. Fuller 		/* Append the region info */
146*7c7c726bSLandon J. Fuller 		error = siba_append_dinfo_region(dinfo, i,
147*7c7c726bSLandon J. Fuller 		    core_id->admatch[i].am_base, core_id->admatch[i].am_size,
148*7c7c726bSLandon J. Fuller 		    bus_reserved);
149*7c7c726bSLandon J. Fuller 		if (error)
150*7c7c726bSLandon J. Fuller 			return (error);
151*7c7c726bSLandon J. Fuller 	}
152*7c7c726bSLandon J. Fuller 
153*7c7c726bSLandon J. Fuller 	/* Register all interrupt(s) */
154*7c7c726bSLandon J. Fuller 	if ((error = siba_register_interrupts(dev, child, dinfo)))
155*7c7c726bSLandon J. Fuller 		return (error);
156*7c7c726bSLandon J. Fuller 
157*7c7c726bSLandon J. Fuller 	return (0);
158*7c7c726bSLandon J. Fuller }
159*7c7c726bSLandon J. Fuller 
160*7c7c726bSLandon J. Fuller 
161*7c7c726bSLandon J. Fuller /**
162*7c7c726bSLandon J. Fuller  * Register and map all interrupts for @p dinfo.
163*7c7c726bSLandon J. Fuller  *
164*7c7c726bSLandon J. Fuller  * @param dev The siba bus device.
165*7c7c726bSLandon J. Fuller  * @param child The siba child device.
166*7c7c726bSLandon J. Fuller  * @param dinfo The device info instance on which to register all interrupt
167*7c7c726bSLandon J. Fuller  * entries.
168*7c7c726bSLandon J. Fuller  */
169*7c7c726bSLandon J. Fuller static int
170*7c7c726bSLandon J. Fuller siba_register_interrupts(device_t dev, device_t child,
171*7c7c726bSLandon J. Fuller      struct siba_devinfo *dinfo)
172*7c7c726bSLandon J. Fuller {
173*7c7c726bSLandon J. Fuller 	int error;
174*7c7c726bSLandon J. Fuller 
175*7c7c726bSLandon J. Fuller 	/* Is backplane interrupt distribution enabled for this core? */
176*7c7c726bSLandon J. Fuller 	if (!dinfo->core_id.intr_en)
177*7c7c726bSLandon J. Fuller 		return (0);
178*7c7c726bSLandon J. Fuller 
179*7c7c726bSLandon J. Fuller 	/* Have one interrupt */
180*7c7c726bSLandon J. Fuller 	dinfo->intr.mapped = false;
181*7c7c726bSLandon J. Fuller 	dinfo->intr.irq = 0;
182*7c7c726bSLandon J. Fuller 	dinfo->intr.rid = -1;
183*7c7c726bSLandon J. Fuller 
184*7c7c726bSLandon J. Fuller 	/* Map the interrupt */
185*7c7c726bSLandon J. Fuller 	error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
186*7c7c726bSLandon J. Fuller 	    &dinfo->intr.irq);
187*7c7c726bSLandon J. Fuller 	if (error) {
188*7c7c726bSLandon J. Fuller 		device_printf(dev, "failed mapping interrupt line for core %u: "
189*7c7c726bSLandon J. Fuller 		    "%d\n", dinfo->core_id.core_info.core_idx, error);
190*7c7c726bSLandon J. Fuller 		return (error);
191*7c7c726bSLandon J. Fuller 	}
192*7c7c726bSLandon J. Fuller 	dinfo->intr.mapped = true;
193*7c7c726bSLandon J. Fuller 
194*7c7c726bSLandon J. Fuller 	/* Update the resource list */
195*7c7c726bSLandon J. Fuller 	dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
196*7c7c726bSLandon J. Fuller 	    dinfo->intr.irq, dinfo->intr.irq, 1);
197*7c7c726bSLandon J. Fuller 
198688fc8c0SLandon J. Fuller 	return (0);
199688fc8c0SLandon J. Fuller }
200688fc8c0SLandon J. Fuller 
201688fc8c0SLandon J. Fuller /**
202caeff9a3SLandon J. Fuller  * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
203caeff9a3SLandon J. Fuller  * number.
2044ad7e9b0SAdrian Chadd  *
20506018a8eSLandon J. Fuller  * @param addrspace Address space index.
2064ad7e9b0SAdrian Chadd  */
20706018a8eSLandon J. Fuller u_int
208caeff9a3SLandon J. Fuller siba_addrspace_device_port(u_int addrspace)
2094ad7e9b0SAdrian Chadd {
21006018a8eSLandon J. Fuller 	/* The first addrspace is always mapped to device0; the remainder
21106018a8eSLandon J. Fuller 	 * are mapped to device1 */
21206018a8eSLandon J. Fuller 	if (addrspace == 0)
21306018a8eSLandon J. Fuller 		return (0);
21406018a8eSLandon J. Fuller 	else
21506018a8eSLandon J. Fuller 		return (1);
2164ad7e9b0SAdrian Chadd }
2174ad7e9b0SAdrian Chadd 
2184ad7e9b0SAdrian Chadd /**
219caeff9a3SLandon J. Fuller  * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
220caeff9a3SLandon J. Fuller  * region number.
2214ad7e9b0SAdrian Chadd  *
22206018a8eSLandon J. Fuller  * @param addrspace Address space index.
22306018a8eSLandon J. Fuller  */
22406018a8eSLandon J. Fuller u_int
225caeff9a3SLandon J. Fuller siba_addrspace_device_region(u_int addrspace)
22606018a8eSLandon J. Fuller {
22706018a8eSLandon J. Fuller 	/* The first addrspace is always mapped to device0.0; the remainder
22806018a8eSLandon J. Fuller 	 * are mapped to device1.0 + (n - 1) */
22906018a8eSLandon J. Fuller 	if (addrspace == 0)
23006018a8eSLandon J. Fuller 		return (0);
23106018a8eSLandon J. Fuller 	else
23206018a8eSLandon J. Fuller 		return (addrspace - 1);
23306018a8eSLandon J. Fuller }
23406018a8eSLandon J. Fuller 
23506018a8eSLandon J. Fuller /**
236caeff9a3SLandon J. Fuller  * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
237caeff9a3SLandon J. Fuller  * number.
23806018a8eSLandon J. Fuller  *
239caeff9a3SLandon J. Fuller  * @param cfg Config block index.
24006018a8eSLandon J. Fuller  */
24106018a8eSLandon J. Fuller u_int
242caeff9a3SLandon J. Fuller siba_cfg_agent_port(u_int cfg)
24306018a8eSLandon J. Fuller {
244caeff9a3SLandon J. Fuller 	/* Always agent0 */
24506018a8eSLandon J. Fuller 	return (0);
24606018a8eSLandon J. Fuller }
24706018a8eSLandon J. Fuller 
24806018a8eSLandon J. Fuller /**
249caeff9a3SLandon J. Fuller  * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
250caeff9a3SLandon J. Fuller  * region number.
25106018a8eSLandon J. Fuller  *
252caeff9a3SLandon J. Fuller  * @param cfg Config block index.
253caeff9a3SLandon J. Fuller  */
254caeff9a3SLandon J. Fuller u_int
255caeff9a3SLandon J. Fuller siba_cfg_agent_region(u_int cfg)
256caeff9a3SLandon J. Fuller {
257caeff9a3SLandon J. Fuller 	/* Always agent0.<idx> */
258caeff9a3SLandon J. Fuller 	return (cfg);
259caeff9a3SLandon J. Fuller }
260caeff9a3SLandon J. Fuller 
261caeff9a3SLandon J. Fuller /**
262caeff9a3SLandon J. Fuller  * Return the number of bhnd(4) ports to advertise for the given
263caeff9a3SLandon J. Fuller  * @p core_id and @p port_type.
26406018a8eSLandon J. Fuller  *
265caeff9a3SLandon J. Fuller  * Refer to the siba_addrspace_index() and siba_cfg_index() functions for
266caeff9a3SLandon J. Fuller  * information on siba's mapping of bhnd(4) port and region identifiers.
267caeff9a3SLandon J. Fuller  *
268caeff9a3SLandon J. Fuller  * @param core_id The siba core info.
269caeff9a3SLandon J. Fuller  * @param port_type The bhnd(4) port type.
270caeff9a3SLandon J. Fuller  */
271caeff9a3SLandon J. Fuller u_int
272caeff9a3SLandon J. Fuller siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)
273caeff9a3SLandon J. Fuller {
274caeff9a3SLandon J. Fuller 	switch (port_type) {
275caeff9a3SLandon J. Fuller 	case BHND_PORT_DEVICE:
276caeff9a3SLandon J. Fuller 		/* 0, 1, or 2 ports */
277*7c7c726bSLandon J. Fuller 		return (min(core_id->num_admatch, 2));
278caeff9a3SLandon J. Fuller 
279caeff9a3SLandon J. Fuller 	case BHND_PORT_AGENT:
280caeff9a3SLandon J. Fuller 		/* One agent port maps all configuration blocks */
281caeff9a3SLandon J. Fuller 		if (core_id->num_cfg_blocks > 0)
282caeff9a3SLandon J. Fuller 			return (1);
283caeff9a3SLandon J. Fuller 
284caeff9a3SLandon J. Fuller 		/* Do not advertise an agent port if there are no configuration
285caeff9a3SLandon J. Fuller 		 * register blocks */
286caeff9a3SLandon J. Fuller 		return (0);
287caeff9a3SLandon J. Fuller 
288caeff9a3SLandon J. Fuller 	default:
289caeff9a3SLandon J. Fuller 		return (0);
290caeff9a3SLandon J. Fuller 	}
291caeff9a3SLandon J. Fuller }
292caeff9a3SLandon J. Fuller 
293caeff9a3SLandon J. Fuller /**
294caeff9a3SLandon J. Fuller  * Return true if @p port of @p port_type is defined by @p core_id, false
295caeff9a3SLandon J. Fuller  * otherwise.
296caeff9a3SLandon J. Fuller  *
297caeff9a3SLandon J. Fuller  * @param core_id The siba core info.
298caeff9a3SLandon J. Fuller  * @param port_type The bhnd(4) port type.
29906018a8eSLandon J. Fuller  * @param port The bhnd(4) port number.
30006018a8eSLandon J. Fuller  */
30106018a8eSLandon J. Fuller bool
302caeff9a3SLandon J. Fuller siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,
303caeff9a3SLandon J. Fuller     u_int port)
30406018a8eSLandon J. Fuller {
30506018a8eSLandon J. Fuller 	/* Verify the index against the port count */
306caeff9a3SLandon J. Fuller 	if (siba_port_count(core_id, port_type) <= port)
30706018a8eSLandon J. Fuller 		return (false);
30806018a8eSLandon J. Fuller 
30906018a8eSLandon J. Fuller 	return (true);
31006018a8eSLandon J. Fuller }
31106018a8eSLandon J. Fuller 
31206018a8eSLandon J. Fuller /**
313caeff9a3SLandon J. Fuller  * Return the number of bhnd(4) regions to advertise for @p core_id on the
314caeff9a3SLandon J. Fuller  * @p port of @p port_type.
315caeff9a3SLandon J. Fuller  *
316caeff9a3SLandon J. Fuller  * @param core_id The siba core info.
317caeff9a3SLandon J. Fuller  * @param port_type The bhnd(4) port type.
318caeff9a3SLandon J. Fuller  */
319caeff9a3SLandon J. Fuller u_int
320caeff9a3SLandon J. Fuller siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
321caeff9a3SLandon J. Fuller     u_int port)
322caeff9a3SLandon J. Fuller {
323caeff9a3SLandon J. Fuller 	/* The port must exist */
324caeff9a3SLandon J. Fuller 	if (!siba_is_port_valid(core_id, port_type, port))
325caeff9a3SLandon J. Fuller 		return (0);
326caeff9a3SLandon J. Fuller 
327caeff9a3SLandon J. Fuller 	switch (port_type) {
328caeff9a3SLandon J. Fuller 	case BHND_PORT_DEVICE:
329caeff9a3SLandon J. Fuller 		/* The first address space, if any, is mapped to device0.0 */
330caeff9a3SLandon J. Fuller 		if (port == 0)
331*7c7c726bSLandon J. Fuller 			return (min(core_id->num_admatch, 1));
332caeff9a3SLandon J. Fuller 
333caeff9a3SLandon J. Fuller 		/* All remaining address spaces are mapped to device0.(n - 1) */
334*7c7c726bSLandon J. Fuller 		if (port == 1 && core_id->num_admatch >= 2)
335*7c7c726bSLandon J. Fuller 			return (core_id->num_admatch - 1);
336caeff9a3SLandon J. Fuller 
337caeff9a3SLandon J. Fuller 		break;
338caeff9a3SLandon J. Fuller 
339caeff9a3SLandon J. Fuller 	case BHND_PORT_AGENT:
340caeff9a3SLandon J. Fuller 		/* All config blocks are mapped to a single port */
341caeff9a3SLandon J. Fuller 		if (port == 0)
342caeff9a3SLandon J. Fuller 			return (core_id->num_cfg_blocks);
343caeff9a3SLandon J. Fuller 
344caeff9a3SLandon J. Fuller 		break;
345caeff9a3SLandon J. Fuller 
346caeff9a3SLandon J. Fuller 	default:
347caeff9a3SLandon J. Fuller 		break;
348caeff9a3SLandon J. Fuller 	}
349caeff9a3SLandon J. Fuller 
350caeff9a3SLandon J. Fuller 	/* Validated above */
351caeff9a3SLandon J. Fuller 	panic("siba_is_port_valid() returned true for unknown %s.%u port",
352caeff9a3SLandon J. Fuller 	    bhnd_port_type_name(port_type), port);
353caeff9a3SLandon J. Fuller 
354caeff9a3SLandon J. Fuller }
355caeff9a3SLandon J. Fuller 
356caeff9a3SLandon J. Fuller /**
357caeff9a3SLandon J. Fuller  * Map a bhnd(4) type/port/region triplet to its associated config block index,
358caeff9a3SLandon J. Fuller  * if any.
359caeff9a3SLandon J. Fuller  *
360caeff9a3SLandon J. Fuller  * We map config registers to port/region identifiers as follows:
361caeff9a3SLandon J. Fuller  *
362caeff9a3SLandon J. Fuller  * 	[port].[region]	[cfg register block]
363caeff9a3SLandon J. Fuller  * 	agent0.0	0
364caeff9a3SLandon J. Fuller  * 	agent0.1	1
365caeff9a3SLandon J. Fuller  *
366caeff9a3SLandon J. Fuller  * @param port_type The bhnd(4) port type.
367caeff9a3SLandon J. Fuller  * @param port The bhnd(4) port number.
368caeff9a3SLandon J. Fuller  * @param region The bhnd(4) port region.
369caeff9a3SLandon J. Fuller  * @param addridx On success, the corresponding addrspace index.
370caeff9a3SLandon J. Fuller  *
371caeff9a3SLandon J. Fuller  * @retval 0 success
372caeff9a3SLandon J. Fuller  * @retval ENOENT if the given type/port/region cannot be mapped to a
373caeff9a3SLandon J. Fuller  * siba config register block.
374caeff9a3SLandon J. Fuller  */
375caeff9a3SLandon J. Fuller int
376caeff9a3SLandon J. Fuller siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,
377caeff9a3SLandon J. Fuller     u_int port, u_int region, u_int *cfgidx)
378caeff9a3SLandon J. Fuller {
379caeff9a3SLandon J. Fuller 	/* Config blocks are mapped to agent ports */
380caeff9a3SLandon J. Fuller 	if (port_type != BHND_PORT_AGENT)
381caeff9a3SLandon J. Fuller 		return (ENOENT);
382caeff9a3SLandon J. Fuller 
383caeff9a3SLandon J. Fuller 	/* Port must be valid */
384caeff9a3SLandon J. Fuller 	if (!siba_is_port_valid(core_id, port_type, port))
385caeff9a3SLandon J. Fuller 		return (ENOENT);
386caeff9a3SLandon J. Fuller 
387caeff9a3SLandon J. Fuller 	if (region >= core_id->num_cfg_blocks)
388caeff9a3SLandon J. Fuller 		return (ENOENT);
389caeff9a3SLandon J. Fuller 
390caeff9a3SLandon J. Fuller 	if (region >= SIBA_MAX_CFG)
391caeff9a3SLandon J. Fuller 		return (ENOENT);
392caeff9a3SLandon J. Fuller 
393caeff9a3SLandon J. Fuller 	/* Found */
394caeff9a3SLandon J. Fuller 	*cfgidx = region;
395caeff9a3SLandon J. Fuller 	return (0);
396caeff9a3SLandon J. Fuller }
397caeff9a3SLandon J. Fuller 
398caeff9a3SLandon J. Fuller /**
399caeff9a3SLandon J. Fuller  * Map an bhnd(4) type/port/region triplet to its associated config block
400caeff9a3SLandon J. Fuller  * entry, if any.
401caeff9a3SLandon J. Fuller  *
402caeff9a3SLandon J. Fuller  * The only supported port type is BHND_PORT_DEVICE.
403caeff9a3SLandon J. Fuller  *
404caeff9a3SLandon J. Fuller  * @param dinfo The device info to search for a matching address space.
405caeff9a3SLandon J. Fuller  * @param type The bhnd(4) port type.
406caeff9a3SLandon J. Fuller  * @param port The bhnd(4) port number.
407caeff9a3SLandon J. Fuller  * @param region The bhnd(4) port region.
408caeff9a3SLandon J. Fuller  */
409caeff9a3SLandon J. Fuller struct siba_cfg_block *
410caeff9a3SLandon J. Fuller siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
411caeff9a3SLandon J. Fuller     u_int region)
412caeff9a3SLandon J. Fuller {
413caeff9a3SLandon J. Fuller 	u_int	cfgidx;
414caeff9a3SLandon J. Fuller 	int	error;
415caeff9a3SLandon J. Fuller 
416caeff9a3SLandon J. Fuller 	/* Map to addrspace index */
417caeff9a3SLandon J. Fuller 	error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);
418caeff9a3SLandon J. Fuller 	if (error)
419caeff9a3SLandon J. Fuller 		return (NULL);
420caeff9a3SLandon J. Fuller 
421caeff9a3SLandon J. Fuller 	/* Found */
422caeff9a3SLandon J. Fuller 	return (&dinfo->cfg[cfgidx]);
423caeff9a3SLandon J. Fuller }
424caeff9a3SLandon J. Fuller 
425caeff9a3SLandon J. Fuller /**
426664a7497SLandon J. Fuller  * Map a bhnd(4) type/port/region triplet to its associated address space
427664a7497SLandon J. Fuller  * index, if any.
42806018a8eSLandon J. Fuller  *
42906018a8eSLandon J. Fuller  * For compatibility with bcma(4), we map address spaces to port/region
43006018a8eSLandon J. Fuller  * identifiers as follows:
43106018a8eSLandon J. Fuller  *
432*7c7c726bSLandon J. Fuller  * 	[port.region]	[admatch index]
43306018a8eSLandon J. Fuller  * 	device0.0	0
43406018a8eSLandon J. Fuller  * 	device1.0	1
43506018a8eSLandon J. Fuller  * 	device1.1	2
43606018a8eSLandon J. Fuller  * 	device1.2	3
43706018a8eSLandon J. Fuller  *
438caeff9a3SLandon J. Fuller  * @param core_id The siba core info.
439caeff9a3SLandon J. Fuller  * @param port_type The bhnd(4) port type.
440664a7497SLandon J. Fuller  * @param port The bhnd(4) port number.
441664a7497SLandon J. Fuller  * @param region The bhnd(4) port region.
442664a7497SLandon J. Fuller  * @param addridx On success, the corresponding addrspace index.
443664a7497SLandon J. Fuller  *
444664a7497SLandon J. Fuller  * @retval 0 success
445664a7497SLandon J. Fuller  * @retval ENOENT if the given type/port/region cannot be mapped to a
446664a7497SLandon J. Fuller  * siba address space.
447664a7497SLandon J. Fuller  */
448664a7497SLandon J. Fuller int
449caeff9a3SLandon J. Fuller siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,
450caeff9a3SLandon J. Fuller     u_int port, u_int region, u_int *addridx)
451664a7497SLandon J. Fuller {
452664a7497SLandon J. Fuller 	u_int idx;
453664a7497SLandon J. Fuller 
454caeff9a3SLandon J. Fuller 	/* Address spaces are always device ports */
455caeff9a3SLandon J. Fuller 	if (port_type != BHND_PORT_DEVICE)
456caeff9a3SLandon J. Fuller 		return (ENOENT);
457caeff9a3SLandon J. Fuller 
458caeff9a3SLandon J. Fuller 	/* Port must be valid */
459caeff9a3SLandon J. Fuller 	if (!siba_is_port_valid(core_id, port_type, port))
460664a7497SLandon J. Fuller 		return (ENOENT);
461664a7497SLandon J. Fuller 
462664a7497SLandon J. Fuller 	if (port == 0)
463664a7497SLandon J. Fuller 		idx = region;
464664a7497SLandon J. Fuller 	else if (port == 1)
465664a7497SLandon J. Fuller 		idx = region + 1;
466664a7497SLandon J. Fuller 	else
467664a7497SLandon J. Fuller 		return (ENOENT);
468664a7497SLandon J. Fuller 
469*7c7c726bSLandon J. Fuller 	if (idx >= core_id->num_admatch)
470664a7497SLandon J. Fuller 		return (ENOENT);
471664a7497SLandon J. Fuller 
472664a7497SLandon J. Fuller 	/* Found */
473664a7497SLandon J. Fuller 	*addridx = idx;
474664a7497SLandon J. Fuller 	return (0);
475664a7497SLandon J. Fuller }
476664a7497SLandon J. Fuller 
477664a7497SLandon J. Fuller /**
478664a7497SLandon J. Fuller  * Map an bhnd(4) type/port/region triplet to its associated address space
479664a7497SLandon J. Fuller  * entry, if any.
480664a7497SLandon J. Fuller  *
481664a7497SLandon J. Fuller  * The only supported port type is BHND_PORT_DEVICE.
482664a7497SLandon J. Fuller  *
48306018a8eSLandon J. Fuller  * @param dinfo The device info to search for a matching address space.
48406018a8eSLandon J. Fuller  * @param type The bhnd(4) port type.
48506018a8eSLandon J. Fuller  * @param port The bhnd(4) port number.
48606018a8eSLandon J. Fuller  * @param region The bhnd(4) port region.
4874ad7e9b0SAdrian Chadd  */
4884ad7e9b0SAdrian Chadd struct siba_addrspace *
48906018a8eSLandon J. Fuller siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
49006018a8eSLandon J. Fuller     u_int region)
4914ad7e9b0SAdrian Chadd {
49206018a8eSLandon J. Fuller 	u_int	addridx;
493664a7497SLandon J. Fuller 	int	error;
4944ad7e9b0SAdrian Chadd 
495664a7497SLandon J. Fuller 	/* Map to addrspace index */
496caeff9a3SLandon J. Fuller 	error = siba_addrspace_index(&dinfo->core_id, type, port, region,
497caeff9a3SLandon J. Fuller 	    &addridx);
498664a7497SLandon J. Fuller 	if (error)
49906018a8eSLandon J. Fuller 		return (NULL);
50006018a8eSLandon J. Fuller 
50106018a8eSLandon J. Fuller 	/* Found */
502664a7497SLandon J. Fuller 	if (addridx >= SIBA_MAX_ADDRSPACE)
503664a7497SLandon J. Fuller 		return (NULL);
504664a7497SLandon J. Fuller 
50506018a8eSLandon J. Fuller 	return (&dinfo->addrspace[addridx]);
5064ad7e9b0SAdrian Chadd }
5074ad7e9b0SAdrian Chadd 
5084ad7e9b0SAdrian Chadd /**
50906018a8eSLandon J. Fuller  * Append an address space entry to @p dinfo.
5104ad7e9b0SAdrian Chadd  *
5114ad7e9b0SAdrian Chadd  * @param dinfo The device info entry to update.
51206018a8eSLandon J. Fuller  * @param addridx The address space index.
5134ad7e9b0SAdrian Chadd  * @param base The mapping's base address.
5144ad7e9b0SAdrian Chadd  * @param size The mapping size.
5154ad7e9b0SAdrian Chadd  * @param bus_reserved Number of bytes to reserve in @p size for bus use
5164ad7e9b0SAdrian Chadd  * when registering the resource list entry. This is used to reserve bus
5174ad7e9b0SAdrian Chadd  * access to the core's SIBA_CFG* register blocks.
5184ad7e9b0SAdrian Chadd  *
5194ad7e9b0SAdrian Chadd  * @retval 0 success
5204ad7e9b0SAdrian Chadd  * @retval non-zero An error occurred appending the entry.
5214ad7e9b0SAdrian Chadd  */
522*7c7c726bSLandon J. Fuller static int
52306018a8eSLandon J. Fuller siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
52406018a8eSLandon J. Fuller     uint32_t base, uint32_t size, uint32_t bus_reserved)
5254ad7e9b0SAdrian Chadd {
5264ad7e9b0SAdrian Chadd 	struct siba_addrspace	*sa;
527f4a3eb02SAdrian Chadd 	rman_res_t		 r_size;
5284ad7e9b0SAdrian Chadd 
5294ad7e9b0SAdrian Chadd 	/* Verify that base + size will not overflow */
5307ba7852fSLandon J. Fuller 	if (size > 0 && UINT32_MAX - (size - 1) < base)
5314ad7e9b0SAdrian Chadd 		return (ERANGE);
5324ad7e9b0SAdrian Chadd 
533f4a3eb02SAdrian Chadd 	/* Verify that size - bus_reserved will not underflow */
534f4a3eb02SAdrian Chadd 	if (size < bus_reserved)
535f4a3eb02SAdrian Chadd 		return (ERANGE);
536f4a3eb02SAdrian Chadd 
5374ad7e9b0SAdrian Chadd 	/* Must not be 0-length */
5384ad7e9b0SAdrian Chadd 	if (size == 0)
5394ad7e9b0SAdrian Chadd 		return (EINVAL);
5404ad7e9b0SAdrian Chadd 
54106018a8eSLandon J. Fuller 	/* Must not exceed addrspace array size */
54206018a8eSLandon J. Fuller 	if (addridx >= nitems(dinfo->addrspace))
5434ad7e9b0SAdrian Chadd 		return (EINVAL);
5444ad7e9b0SAdrian Chadd 
54506018a8eSLandon J. Fuller 	/* Initialize new addrspace entry */
54606018a8eSLandon J. Fuller 	sa = &dinfo->addrspace[addridx];
5474ad7e9b0SAdrian Chadd 	sa->sa_base = base;
5484ad7e9b0SAdrian Chadd 	sa->sa_size = size;
549f4a3eb02SAdrian Chadd 	sa->sa_bus_reserved = bus_reserved;
5504ad7e9b0SAdrian Chadd 
5514ad7e9b0SAdrian Chadd 	/* Populate the resource list */
552f4a3eb02SAdrian Chadd 	r_size = size - bus_reserved;
5534ad7e9b0SAdrian Chadd 	sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
5547ba7852fSLandon J. Fuller 	    base, base + (r_size - 1), r_size);
5554ad7e9b0SAdrian Chadd 
5564ad7e9b0SAdrian Chadd 	return (0);
5574ad7e9b0SAdrian Chadd }
5584ad7e9b0SAdrian Chadd 
5594ad7e9b0SAdrian Chadd /**
5604ad7e9b0SAdrian Chadd  * Deallocate the given device info structure and any associated resources.
5614ad7e9b0SAdrian Chadd  *
5624ad7e9b0SAdrian Chadd  * @param dev The requesting bus device.
563caeff9a3SLandon J. Fuller  * @param child The siba child device.
564caeff9a3SLandon J. Fuller  * @param dinfo Device info associated with @p child to be deallocated.
5654ad7e9b0SAdrian Chadd  */
5664ad7e9b0SAdrian Chadd void
567caeff9a3SLandon J. Fuller siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)
5684ad7e9b0SAdrian Chadd {
5694ad7e9b0SAdrian Chadd 	resource_list_free(&dinfo->resources);
5704ad7e9b0SAdrian Chadd 
5714ad7e9b0SAdrian Chadd 	/* Free all mapped configuration blocks */
5724ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
573caeff9a3SLandon J. Fuller 		if (dinfo->cfg_res[i] == NULL)
5744ad7e9b0SAdrian Chadd 			continue;
5754ad7e9b0SAdrian Chadd 
5764ad7e9b0SAdrian Chadd 		bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],
577caeff9a3SLandon J. Fuller 		    dinfo->cfg_res[i]);
5784ad7e9b0SAdrian Chadd 
579caeff9a3SLandon J. Fuller 		dinfo->cfg_res[i] = NULL;
5804ad7e9b0SAdrian Chadd 		dinfo->cfg_rid[i] = -1;
5814ad7e9b0SAdrian Chadd 	}
5824ad7e9b0SAdrian Chadd 
583caeff9a3SLandon J. Fuller 	/* Unmap the core's interrupt */
584*7c7c726bSLandon J. Fuller 	if (dinfo->core_id.intr_en && dinfo->intr.mapped) {
585caeff9a3SLandon J. Fuller 		BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);
586caeff9a3SLandon J. Fuller 		dinfo->intr.mapped = false;
587caeff9a3SLandon J. Fuller 	}
588caeff9a3SLandon J. Fuller 
5894ad7e9b0SAdrian Chadd 	free(dinfo, M_BHND);
5904ad7e9b0SAdrian Chadd }
5914ad7e9b0SAdrian Chadd 
5924ad7e9b0SAdrian Chadd /**
5934ad7e9b0SAdrian Chadd  * Return the core-enumeration-relative offset for the @p addrspace
5944ad7e9b0SAdrian Chadd  * SIBA_R0_ADMATCH* register.
5954ad7e9b0SAdrian Chadd  *
5964ad7e9b0SAdrian Chadd  * @param addrspace The address space index.
5974ad7e9b0SAdrian Chadd  *
5984ad7e9b0SAdrian Chadd  * @retval non-zero success
5994ad7e9b0SAdrian Chadd  * @retval 0 the given @p addrspace index is not supported.
6004ad7e9b0SAdrian Chadd  */
6014ad7e9b0SAdrian Chadd u_int
6024ad7e9b0SAdrian Chadd siba_admatch_offset(uint8_t addrspace)
6034ad7e9b0SAdrian Chadd {
6044ad7e9b0SAdrian Chadd 	switch (addrspace) {
6054ad7e9b0SAdrian Chadd 	case 0:
6064ad7e9b0SAdrian Chadd 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);
6074ad7e9b0SAdrian Chadd 	case 1:
6084ad7e9b0SAdrian Chadd 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);
6094ad7e9b0SAdrian Chadd 	case 2:
6104ad7e9b0SAdrian Chadd 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);
6114ad7e9b0SAdrian Chadd 	case 3:
6124ad7e9b0SAdrian Chadd 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);
6134ad7e9b0SAdrian Chadd 	default:
6144ad7e9b0SAdrian Chadd 		return (0);
6154ad7e9b0SAdrian Chadd 	}
6164ad7e9b0SAdrian Chadd }
6174ad7e9b0SAdrian Chadd 
6184ad7e9b0SAdrian Chadd /**
6194ad7e9b0SAdrian Chadd  * Parse a SIBA_R0_ADMATCH* register.
6204ad7e9b0SAdrian Chadd  *
6214ad7e9b0SAdrian Chadd  * @param addrspace The address space index.
6224ad7e9b0SAdrian Chadd  * @param am The address match register value to be parsed.
623*7c7c726bSLandon J. Fuller  * @param[out] admatch The parsed address match descriptor
6244ad7e9b0SAdrian Chadd  *
6254ad7e9b0SAdrian Chadd  * @retval 0 success
626453130d9SPedro F. Giffuni  * @retval non-zero a parse error occurred.
6274ad7e9b0SAdrian Chadd  */
6284ad7e9b0SAdrian Chadd int
629*7c7c726bSLandon J. Fuller siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)
6304ad7e9b0SAdrian Chadd {
6314ad7e9b0SAdrian Chadd 	u_int am_type;
6324ad7e9b0SAdrian Chadd 
6334ad7e9b0SAdrian Chadd 	/* Extract the base address and size */
6344ad7e9b0SAdrian Chadd 	am_type = SIBA_REG_GET(am, AM_TYPE);
6354ad7e9b0SAdrian Chadd 	switch (am_type) {
6364ad7e9b0SAdrian Chadd 	case 0:
637*7c7c726bSLandon J. Fuller 		/* Type 0 entries are always enabled, and do not support
638*7c7c726bSLandon J. Fuller 		 * negative matching */
639*7c7c726bSLandon J. Fuller 		admatch->am_base = am & SIBA_AM_BASE0_MASK;
640*7c7c726bSLandon J. Fuller 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
641*7c7c726bSLandon J. Fuller 		admatch->am_enabled = true;
642*7c7c726bSLandon J. Fuller 		admatch->am_negative = false;
6434ad7e9b0SAdrian Chadd 		break;
6444ad7e9b0SAdrian Chadd 	case 1:
645*7c7c726bSLandon J. Fuller 		admatch->am_base = am & SIBA_AM_BASE1_MASK;
646*7c7c726bSLandon J. Fuller 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
647*7c7c726bSLandon J. Fuller 		admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
648*7c7c726bSLandon J. Fuller 		admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
6494ad7e9b0SAdrian Chadd 		break;
6504ad7e9b0SAdrian Chadd 	case 2:
651*7c7c726bSLandon J. Fuller 		admatch->am_base = am & SIBA_AM_BASE2_MASK;
652*7c7c726bSLandon J. Fuller 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
653*7c7c726bSLandon J. Fuller 		admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
654*7c7c726bSLandon J. Fuller 		admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
6554ad7e9b0SAdrian Chadd 		break;
6564ad7e9b0SAdrian Chadd 	default:
6574ad7e9b0SAdrian Chadd 		return (EINVAL);
6584ad7e9b0SAdrian Chadd 	}
6594ad7e9b0SAdrian Chadd 
6604ad7e9b0SAdrian Chadd 	return (0);
6614ad7e9b0SAdrian Chadd }
6628a03f98aSLandon J. Fuller 
6638a03f98aSLandon J. Fuller /**
664ac59515bSLandon J. Fuller  * Write @p value to @p dev's CFG0 target/initiator state register, performing
665ac59515bSLandon J. Fuller  * required read-back and waiting for completion.
6668a03f98aSLandon J. Fuller  *
6678a03f98aSLandon J. Fuller  * @param dev The siba(4) child device.
668ac59515bSLandon J. Fuller  * @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,
6698a03f98aSLandon J. Fuller  * SIBA_CFG0_IMSTATE)
6708a03f98aSLandon J. Fuller  * @param value The value to write to @p reg.
6718a03f98aSLandon J. Fuller  * @param mask The mask of bits to be included from @p value.
6728a03f98aSLandon J. Fuller  */
673ac59515bSLandon J. Fuller void
6748a03f98aSLandon J. Fuller siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
6758a03f98aSLandon J. Fuller     bus_size_t reg, uint32_t value, uint32_t mask)
6768a03f98aSLandon J. Fuller {
6778a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
6788a03f98aSLandon J. Fuller 	uint32_t		 rval;
6798a03f98aSLandon J. Fuller 
680ac59515bSLandon J. Fuller 	r = dinfo->cfg_res[0];
6818a03f98aSLandon J. Fuller 
682ac59515bSLandon J. Fuller 	KASSERT(r != NULL, ("%s missing CFG0 mapping",
683ac59515bSLandon J. Fuller 	    device_get_nameunit(dev)));
684ac59515bSLandon J. Fuller 	KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",
685ac59515bSLandon J. Fuller 	    device_get_nameunit(dev), (uintmax_t)reg));
6868a03f98aSLandon J. Fuller 
6878a03f98aSLandon J. Fuller 	rval = bhnd_bus_read_4(r, reg);
6888a03f98aSLandon J. Fuller 	rval &= ~mask;
6898a03f98aSLandon J. Fuller 	rval |= (value & mask);
6908a03f98aSLandon J. Fuller 
6918a03f98aSLandon J. Fuller 	bhnd_bus_write_4(r, reg, rval);
6928a03f98aSLandon J. Fuller 	bhnd_bus_read_4(r, reg); /* read-back */
6938a03f98aSLandon J. Fuller 	DELAY(1);
6948a03f98aSLandon J. Fuller }
6958a03f98aSLandon J. Fuller 
6968a03f98aSLandon J. Fuller /**
697ac59515bSLandon J. Fuller  * Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state
698ac59515bSLandon J. Fuller  * register value to be equal to @p value after applying @p mask bits to both
699ac59515bSLandon J. Fuller  * values.
7008a03f98aSLandon J. Fuller  *
7018a03f98aSLandon J. Fuller  * @param dev The siba(4) child device to wait on.
7028a03f98aSLandon J. Fuller  * @param dinfo The @p dev's device info
703ac59515bSLandon J. Fuller  * @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,
704ac59515bSLandon J. Fuller  * SIBA_CFG0_IMSTATE)
705ac59515bSLandon J. Fuller  * @param value The value against which @p reg will be compared.
706ac59515bSLandon J. Fuller  * @param mask The mask to be applied when comparing @p value with @p reg.
707ac59515bSLandon J. Fuller  * @param usec The maximum number of microseconds to wait for completion.
7088a03f98aSLandon J. Fuller  *
7098a03f98aSLandon J. Fuller  * @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.
7108a03f98aSLandon J. Fuller  * @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
711ac59515bSLandon J. Fuller  * @retval ETIMEDOUT if a timeout occurs.
7128a03f98aSLandon J. Fuller  */
7138a03f98aSLandon J. Fuller int
714ac59515bSLandon J. Fuller siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,
715ac59515bSLandon J. Fuller     uint32_t value, uint32_t mask, u_int usec)
7168a03f98aSLandon J. Fuller {
7178a03f98aSLandon J. Fuller 	struct bhnd_resource	*r;
718ac59515bSLandon J. Fuller 	uint32_t		 rval;
7198a03f98aSLandon J. Fuller 
720caeff9a3SLandon J. Fuller 	if ((r = dinfo->cfg_res[0]) == NULL)
7218a03f98aSLandon J. Fuller 		return (ENODEV);
7228a03f98aSLandon J. Fuller 
723ac59515bSLandon J. Fuller 	value &= mask;
7248a03f98aSLandon J. Fuller 	for (int i = 0; i < usec; i += 10) {
725ac59515bSLandon J. Fuller 		rval = bhnd_bus_read_4(r, reg);
726ac59515bSLandon J. Fuller 		if ((rval & mask) == value)
7278a03f98aSLandon J. Fuller 			return (0);
7288a03f98aSLandon J. Fuller 
7298a03f98aSLandon J. Fuller 		DELAY(10);
7308a03f98aSLandon J. Fuller 	}
7318a03f98aSLandon J. Fuller 
7328a03f98aSLandon J. Fuller 	return (ETIMEDOUT);
7338a03f98aSLandon J. Fuller }
734