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