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