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