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