xref: /freebsd/sys/dev/bhnd/siba/siba_subr.c (revision ca942ab4b2b665e5375cc72afc4930d3b33b14f9)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/limits.h>
38 #include <sys/systm.h>
39 
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42 
43 #include <dev/bhnd/bhndvar.h>
44 
45 #include "sibareg.h"
46 #include "sibavar.h"
47 
48 static int	siba_register_interrupts(device_t dev, device_t child,
49 		    struct siba_devinfo *dinfo);
50 static int	siba_append_dinfo_region(struct siba_devinfo *dinfo,
51 		     uint8_t addridx, uint32_t base, uint32_t size,
52 		     uint32_t bus_reserved);
53 
54 /**
55  * Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor
56  * code.
57  *
58  * @param ocp_vendor An OCP vendor code.
59  * @return The BHND_MFGID constant corresponding to @p ocp_vendor, or
60  * BHND_MFGID_INVALID if the OCP vendor is unknown.
61  */
62 uint16_t
siba_get_bhnd_mfgid(uint16_t ocp_vendor)63 siba_get_bhnd_mfgid(uint16_t ocp_vendor)
64 {
65 	switch (ocp_vendor) {
66 	case OCP_VENDOR_BCM:
67 		return (BHND_MFGID_BCM);
68 	default:
69 		return (BHND_MFGID_INVALID);
70 	}
71 }
72 
73 /**
74  * Allocate and return a new empty device info structure.
75  *
76  * @param bus The requesting bus device.
77  *
78  * @retval NULL if allocation failed.
79  */
80 struct siba_devinfo *
siba_alloc_dinfo(device_t bus)81 siba_alloc_dinfo(device_t bus)
82 {
83 	struct siba_devinfo *dinfo;
84 
85 	dinfo = malloc(sizeof(struct siba_devinfo), M_BHND, M_NOWAIT|M_ZERO);
86 	if (dinfo == NULL)
87 		return NULL;
88 
89 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
90 		dinfo->cfg[i] = ((struct siba_cfg_block){
91 			.cb_base = 0,
92 			.cb_size = 0,
93 			.cb_rid = -1,
94 		});
95 		dinfo->cfg_res[i] = NULL;
96 	}
97 
98 	resource_list_init(&dinfo->resources);
99 
100 	dinfo->pmu_state = SIBA_PMU_NONE;
101 
102 	dinfo->intr = (struct siba_intr) {
103 		.mapped = false,
104 		.rid = -1
105 	};
106 
107 	return dinfo;
108 }
109 
110 /**
111  * Initialize a device info structure previously allocated via
112  * siba_alloc_dinfo, copying the provided core id.
113  *
114  * @param dev The requesting bus device.
115  * @param child The siba child device.
116  * @param dinfo The device info instance.
117  * @param core Device core info.
118  *
119  * @retval 0 success
120  * @retval non-zero initialization failed.
121  */
122 int
siba_init_dinfo(device_t dev,device_t child,struct siba_devinfo * dinfo,const struct siba_core_id * core_id)123 siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,
124     const struct siba_core_id *core_id)
125 {
126 	int error;
127 
128 	dinfo->core_id = *core_id;
129 
130 	/* Register all address space mappings */
131 	for (uint8_t i = 0; i < core_id->num_admatch; i++) {
132 		uint32_t bus_reserved;
133 
134 		/* If this is the device's core/enumeration addrespace,
135 		 * reserve the Sonics configuration register blocks for the
136 		 * use of our bus. */
137 		bus_reserved = 0;
138 		if (i == SIBA_CORE_ADDRSPACE)
139 			bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;
140 
141 		/* Append the region info */
142 		error = siba_append_dinfo_region(dinfo, i,
143 		    core_id->admatch[i].am_base, core_id->admatch[i].am_size,
144 		    bus_reserved);
145 		if (error)
146 			return (error);
147 	}
148 
149 	/* Register all interrupt(s) */
150 	if ((error = siba_register_interrupts(dev, child, dinfo)))
151 		return (error);
152 
153 	return (0);
154 }
155 
156 /**
157  * Register and map all interrupts for @p dinfo.
158  *
159  * @param dev The siba bus device.
160  * @param child The siba child device.
161  * @param dinfo The device info instance on which to register all interrupt
162  * entries.
163  */
164 static int
siba_register_interrupts(device_t dev,device_t child,struct siba_devinfo * dinfo)165 siba_register_interrupts(device_t dev, device_t child,
166      struct siba_devinfo *dinfo)
167 {
168 	int error;
169 
170 	/* Is backplane interrupt distribution enabled for this core? */
171 	if (!dinfo->core_id.intr_en)
172 		return (0);
173 
174 	/* Have one interrupt */
175 	dinfo->intr.mapped = false;
176 	dinfo->intr.irq = 0;
177 	dinfo->intr.rid = -1;
178 
179 	/* Map the interrupt */
180 	error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,
181 	    &dinfo->intr.irq);
182 	if (error) {
183 		device_printf(dev, "failed mapping interrupt line for core %u: "
184 		    "%d\n", dinfo->core_id.core_info.core_idx, error);
185 		return (error);
186 	}
187 	dinfo->intr.mapped = true;
188 
189 	/* Update the resource list */
190 	dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,
191 	    dinfo->intr.irq, dinfo->intr.irq, 1);
192 
193 	return (0);
194 }
195 
196 /**
197  * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
198  * number.
199  *
200  * @param addrspace Address space index.
201  */
202 u_int
siba_addrspace_device_port(u_int addrspace)203 siba_addrspace_device_port(u_int addrspace)
204 {
205 	/* The first addrspace is always mapped to device0; the remainder
206 	 * are mapped to device1 */
207 	if (addrspace == 0)
208 		return (0);
209 	else
210 		return (1);
211 }
212 
213 /**
214  * Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port
215  * region number.
216  *
217  * @param addrspace Address space index.
218  */
219 u_int
siba_addrspace_device_region(u_int addrspace)220 siba_addrspace_device_region(u_int addrspace)
221 {
222 	/* The first addrspace is always mapped to device0.0; the remainder
223 	 * are mapped to device1.0 + (n - 1) */
224 	if (addrspace == 0)
225 		return (0);
226 	else
227 		return (addrspace - 1);
228 }
229 
230 /**
231  * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
232  * number.
233  *
234  * @param cfg Config block index.
235  */
236 u_int
siba_cfg_agent_port(u_int cfg)237 siba_cfg_agent_port(u_int cfg)
238 {
239 	/* Always agent0 */
240 	return (0);
241 }
242 
243 /**
244  * Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port
245  * region number.
246  *
247  * @param cfg Config block index.
248  */
249 u_int
siba_cfg_agent_region(u_int cfg)250 siba_cfg_agent_region(u_int cfg)
251 {
252 	/* Always agent0.<idx> */
253 	return (cfg);
254 }
255 
256 /**
257  * Return the number of bhnd(4) ports to advertise for the given
258  * @p core_id and @p port_type.
259  *
260  * Refer to the siba_addrspace_index() and siba_cfg_index() functions for
261  * information on siba's mapping of bhnd(4) port and region identifiers.
262  *
263  * @param core_id The siba core info.
264  * @param port_type The bhnd(4) port type.
265  */
266 u_int
siba_port_count(struct siba_core_id * core_id,bhnd_port_type port_type)267 siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)
268 {
269 	switch (port_type) {
270 	case BHND_PORT_DEVICE:
271 		/* 0, 1, or 2 ports */
272 		return (min(core_id->num_admatch, 2));
273 
274 	case BHND_PORT_AGENT:
275 		/* One agent port maps all configuration blocks */
276 		if (core_id->num_cfg_blocks > 0)
277 			return (1);
278 
279 		/* Do not advertise an agent port if there are no configuration
280 		 * register blocks */
281 		return (0);
282 
283 	default:
284 		return (0);
285 	}
286 }
287 
288 /**
289  * Return true if @p port of @p port_type is defined by @p core_id, false
290  * otherwise.
291  *
292  * @param core_id The siba core info.
293  * @param port_type The bhnd(4) port type.
294  * @param port The bhnd(4) port number.
295  */
296 bool
siba_is_port_valid(struct siba_core_id * core_id,bhnd_port_type port_type,u_int port)297 siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,
298     u_int port)
299 {
300 	/* Verify the index against the port count */
301 	if (siba_port_count(core_id, port_type) <= port)
302 		return (false);
303 
304 	return (true);
305 }
306 
307 /**
308  * Return the number of bhnd(4) regions to advertise for @p core_id on the
309  * @p port of @p port_type.
310  *
311  * @param core_id The siba core info.
312  * @param port_type The bhnd(4) port type.
313  */
314 u_int
siba_port_region_count(struct siba_core_id * core_id,bhnd_port_type port_type,u_int port)315 siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,
316     u_int port)
317 {
318 	/* The port must exist */
319 	if (!siba_is_port_valid(core_id, port_type, port))
320 		return (0);
321 
322 	switch (port_type) {
323 	case BHND_PORT_DEVICE:
324 		/* The first address space, if any, is mapped to device0.0 */
325 		if (port == 0)
326 			return (min(core_id->num_admatch, 1));
327 
328 		/* All remaining address spaces are mapped to device0.(n - 1) */
329 		if (port == 1 && core_id->num_admatch >= 2)
330 			return (core_id->num_admatch - 1);
331 
332 		break;
333 
334 	case BHND_PORT_AGENT:
335 		/* All config blocks are mapped to a single port */
336 		if (port == 0)
337 			return (core_id->num_cfg_blocks);
338 
339 		break;
340 
341 	default:
342 		break;
343 	}
344 
345 	/* Validated above */
346 	panic("siba_is_port_valid() returned true for unknown %s.%u port",
347 	    bhnd_port_type_name(port_type), port);
348 
349 }
350 
351 /**
352  * Map a bhnd(4) type/port/region triplet to its associated config block index,
353  * if any.
354  *
355  * We map config registers to port/region identifiers as follows:
356  *
357  * 	[port].[region]	[cfg register block]
358  * 	agent0.0	0
359  * 	agent0.1	1
360  *
361  * @param port_type The bhnd(4) port type.
362  * @param port The bhnd(4) port number.
363  * @param region The bhnd(4) port region.
364  * @param addridx On success, the corresponding addrspace index.
365  *
366  * @retval 0 success
367  * @retval ENOENT if the given type/port/region cannot be mapped to a
368  * siba config register block.
369  */
370 int
siba_cfg_index(struct siba_core_id * core_id,bhnd_port_type port_type,u_int port,u_int region,u_int * cfgidx)371 siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,
372     u_int port, u_int region, u_int *cfgidx)
373 {
374 	/* Config blocks are mapped to agent ports */
375 	if (port_type != BHND_PORT_AGENT)
376 		return (ENOENT);
377 
378 	/* Port must be valid */
379 	if (!siba_is_port_valid(core_id, port_type, port))
380 		return (ENOENT);
381 
382 	if (region >= core_id->num_cfg_blocks)
383 		return (ENOENT);
384 
385 	if (region >= SIBA_MAX_CFG)
386 		return (ENOENT);
387 
388 	/* Found */
389 	*cfgidx = region;
390 	return (0);
391 }
392 
393 /**
394  * Map an bhnd(4) type/port/region triplet to its associated config block
395  * entry, if any.
396  *
397  * The only supported port type is BHND_PORT_DEVICE.
398  *
399  * @param dinfo The device info to search for a matching address space.
400  * @param type The bhnd(4) port type.
401  * @param port The bhnd(4) port number.
402  * @param region The bhnd(4) port region.
403  */
404 struct siba_cfg_block *
siba_find_cfg_block(struct siba_devinfo * dinfo,bhnd_port_type type,u_int port,u_int region)405 siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
406     u_int region)
407 {
408 	u_int	cfgidx;
409 	int	error;
410 
411 	/* Map to addrspace index */
412 	error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);
413 	if (error)
414 		return (NULL);
415 
416 	/* Found */
417 	return (&dinfo->cfg[cfgidx]);
418 }
419 
420 /**
421  * Map a bhnd(4) type/port/region triplet to its associated address space
422  * index, if any.
423  *
424  * For compatibility with bcma(4), we map address spaces to port/region
425  * identifiers as follows:
426  *
427  * 	[port.region]	[admatch index]
428  * 	device0.0	0
429  * 	device1.0	1
430  * 	device1.1	2
431  * 	device1.2	3
432  *
433  * @param core_id The siba core info.
434  * @param port_type The bhnd(4) port type.
435  * @param port The bhnd(4) port number.
436  * @param region The bhnd(4) port region.
437  * @param addridx On success, the corresponding addrspace index.
438  *
439  * @retval 0 success
440  * @retval ENOENT if the given type/port/region cannot be mapped to a
441  * siba address space.
442  */
443 int
siba_addrspace_index(struct siba_core_id * core_id,bhnd_port_type port_type,u_int port,u_int region,u_int * addridx)444 siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,
445     u_int port, u_int region, u_int *addridx)
446 {
447 	u_int idx;
448 
449 	/* Address spaces are always device ports */
450 	if (port_type != BHND_PORT_DEVICE)
451 		return (ENOENT);
452 
453 	/* Port must be valid */
454 	if (!siba_is_port_valid(core_id, port_type, port))
455 		return (ENOENT);
456 
457 	if (port == 0)
458 		idx = region;
459 	else if (port == 1)
460 		idx = region + 1;
461 	else
462 		return (ENOENT);
463 
464 	if (idx >= core_id->num_admatch)
465 		return (ENOENT);
466 
467 	/* Found */
468 	*addridx = idx;
469 	return (0);
470 }
471 
472 /**
473  * Map an bhnd(4) type/port/region triplet to its associated address space
474  * entry, if any.
475  *
476  * The only supported port type is BHND_PORT_DEVICE.
477  *
478  * @param dinfo The device info to search for a matching address space.
479  * @param type The bhnd(4) port type.
480  * @param port The bhnd(4) port number.
481  * @param region The bhnd(4) port region.
482  */
483 struct siba_addrspace *
siba_find_addrspace(struct siba_devinfo * dinfo,bhnd_port_type type,u_int port,u_int region)484 siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,
485     u_int region)
486 {
487 	u_int	addridx;
488 	int	error;
489 
490 	/* Map to addrspace index */
491 	error = siba_addrspace_index(&dinfo->core_id, type, port, region,
492 	    &addridx);
493 	if (error)
494 		return (NULL);
495 
496 	/* Found */
497 	if (addridx >= SIBA_MAX_ADDRSPACE)
498 		return (NULL);
499 
500 	return (&dinfo->addrspace[addridx]);
501 }
502 
503 /**
504  * Append an address space entry to @p dinfo.
505  *
506  * @param dinfo The device info entry to update.
507  * @param addridx The address space index.
508  * @param base The mapping's base address.
509  * @param size The mapping size.
510  * @param bus_reserved Number of bytes to reserve in @p size for bus use
511  * when registering the resource list entry. This is used to reserve bus
512  * access to the core's SIBA_CFG* register blocks.
513  *
514  * @retval 0 success
515  * @retval non-zero An error occurred appending the entry.
516  */
517 static int
siba_append_dinfo_region(struct siba_devinfo * dinfo,uint8_t addridx,uint32_t base,uint32_t size,uint32_t bus_reserved)518 siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,
519     uint32_t base, uint32_t size, uint32_t bus_reserved)
520 {
521 	struct siba_addrspace	*sa;
522 	rman_res_t		 r_size;
523 
524 	/* Verify that base + size will not overflow */
525 	if (size > 0 && UINT32_MAX - (size - 1) < base)
526 		return (ERANGE);
527 
528 	/* Verify that size - bus_reserved will not underflow */
529 	if (size < bus_reserved)
530 		return (ERANGE);
531 
532 	/* Must not be 0-length */
533 	if (size == 0)
534 		return (EINVAL);
535 
536 	/* Must not exceed addrspace array size */
537 	if (addridx >= nitems(dinfo->addrspace))
538 		return (EINVAL);
539 
540 	/* Initialize new addrspace entry */
541 	sa = &dinfo->addrspace[addridx];
542 	sa->sa_base = base;
543 	sa->sa_size = size;
544 	sa->sa_bus_reserved = bus_reserved;
545 
546 	/* Populate the resource list */
547 	r_size = size - bus_reserved;
548 	sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
549 	    base, base + (r_size - 1), r_size);
550 
551 	return (0);
552 }
553 
554 /**
555  * Deallocate the given device info structure and any associated resources.
556  *
557  * @param dev The requesting bus device.
558  * @param child The siba child device.
559  * @param dinfo Device info associated with @p child to be deallocated.
560  */
561 void
siba_free_dinfo(device_t dev,device_t child,struct siba_devinfo * dinfo)562 siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)
563 {
564 	resource_list_free(&dinfo->resources);
565 
566 	/* Free all mapped configuration blocks */
567 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
568 		if (dinfo->cfg_res[i] == NULL)
569 			continue;
570 
571 		bhnd_release_resource(dev, dinfo->cfg_res[i]);
572 
573 		dinfo->cfg_res[i] = NULL;
574 	}
575 
576 	/* Unmap the core's interrupt */
577 	if (dinfo->core_id.intr_en && dinfo->intr.mapped) {
578 		BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);
579 		dinfo->intr.mapped = false;
580 	}
581 
582 	free(dinfo, M_BHND);
583 }
584 
585 /**
586  * Return the core-enumeration-relative offset for the @p addrspace
587  * SIBA_R0_ADMATCH* register.
588  *
589  * @param addrspace The address space index.
590  *
591  * @retval non-zero success
592  * @retval 0 the given @p addrspace index is not supported.
593  */
594 u_int
siba_admatch_offset(uint8_t addrspace)595 siba_admatch_offset(uint8_t addrspace)
596 {
597 	switch (addrspace) {
598 	case 0:
599 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);
600 	case 1:
601 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);
602 	case 2:
603 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);
604 	case 3:
605 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);
606 	default:
607 		return (0);
608 	}
609 }
610 
611 /**
612  * Parse a SIBA_R0_ADMATCH* register.
613  *
614  * @param addrspace The address space index.
615  * @param am The address match register value to be parsed.
616  * @param[out] admatch The parsed address match descriptor
617  *
618  * @retval 0 success
619  * @retval non-zero a parse error occurred.
620  */
621 int
siba_parse_admatch(uint32_t am,struct siba_admatch * admatch)622 siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)
623 {
624 	u_int am_type;
625 
626 	/* Extract the base address and size */
627 	am_type = SIBA_REG_GET(am, AM_TYPE);
628 	switch (am_type) {
629 	case 0:
630 		/* Type 0 entries are always enabled, and do not support
631 		 * negative matching */
632 		admatch->am_base = am & SIBA_AM_BASE0_MASK;
633 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
634 		admatch->am_enabled = true;
635 		admatch->am_negative = false;
636 		break;
637 	case 1:
638 		admatch->am_base = am & SIBA_AM_BASE1_MASK;
639 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
640 		admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
641 		admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
642 		break;
643 	case 2:
644 		admatch->am_base = am & SIBA_AM_BASE2_MASK;
645 		admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
646 		admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);
647 		admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);
648 		break;
649 	default:
650 		return (EINVAL);
651 	}
652 
653 	return (0);
654 }
655 
656 /**
657  * Write @p value to @p dev's CFG0 target/initiator state register, performing
658  * required read-back and waiting for completion.
659  *
660  * @param dev The siba(4) child device.
661  * @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,
662  * SIBA_CFG0_IMSTATE)
663  * @param value The value to write to @p reg.
664  * @param mask The mask of bits to be included from @p value.
665  */
666 void
siba_write_target_state(device_t dev,struct siba_devinfo * dinfo,bus_size_t reg,uint32_t value,uint32_t mask)667 siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,
668     bus_size_t reg, uint32_t value, uint32_t mask)
669 {
670 	struct bhnd_resource	*r;
671 	uint32_t		 rval;
672 
673 	r = dinfo->cfg_res[0];
674 
675 	KASSERT(r != NULL, ("%s missing CFG0 mapping",
676 	    device_get_nameunit(dev)));
677 	KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",
678 	    device_get_nameunit(dev), (uintmax_t)reg));
679 
680 	rval = bhnd_bus_read_4(r, reg);
681 	rval &= ~mask;
682 	rval |= (value & mask);
683 
684 	bhnd_bus_write_4(r, reg, rval);
685 	bhnd_bus_read_4(r, reg); /* read-back */
686 	DELAY(1);
687 }
688 
689 /**
690  * Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state
691  * register value to be equal to @p value after applying @p mask bits to both
692  * values.
693  *
694  * @param dev The siba(4) child device to wait on.
695  * @param dinfo The @p dev's device info
696  * @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,
697  * SIBA_CFG0_IMSTATE)
698  * @param value The value against which @p reg will be compared.
699  * @param mask The mask to be applied when comparing @p value with @p reg.
700  * @param usec The maximum number of microseconds to wait for completion.
701  *
702  * @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.
703  * @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.
704  * @retval ETIMEDOUT if a timeout occurs.
705  */
706 int
siba_wait_target_state(device_t dev,struct siba_devinfo * dinfo,bus_size_t reg,uint32_t value,uint32_t mask,u_int usec)707 siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,
708     uint32_t value, uint32_t mask, u_int usec)
709 {
710 	struct bhnd_resource	*r;
711 	uint32_t		 rval;
712 
713 	if ((r = dinfo->cfg_res[0]) == NULL)
714 		return (ENODEV);
715 
716 	value &= mask;
717 	for (int i = 0; i < usec; i += 10) {
718 		rval = bhnd_bus_read_4(r, reg);
719 		if ((rval & mask) == value)
720 			return (0);
721 
722 		DELAY(10);
723 	}
724 
725 	return (ETIMEDOUT);
726 }
727