xref: /illumos-gate/usr/src/uts/sun4/io/px/px_util.c (revision 80ab886d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * PCI nexus utility routines:
30  *	property and config routines for attach()
31  *	reg/intr/range/assigned-address property routines for bus_map()
32  *	init_child()
33  *	fault handling
34  */
35 
36 #include <sys/types.h>
37 #include <sys/kmem.h>
38 #include <sys/async.h>
39 #include <sys/sysmacros.h>
40 #include <sys/sunddi.h>
41 #include <sys/sunndi.h>
42 #include <sys/ddi_impldefs.h>
43 #include "px_obj.h"
44 #include "pcie_pwr.h"
45 
46 /*LINTLIBRARY*/
47 
48 /*
49  * px_get_props
50  *
51  * This function is called from the attach routine to get the key
52  * properties of the pci nodes.
53  *
54  * used by: px_attach()
55  *
56  * return value: DDI_FAILURE on failure
57  */
58 int
59 px_get_props(px_t *px_p, dev_info_t *dip)
60 {
61 	int i, no_of_intrs;
62 
63 	/*
64 	 * Get the bus-ranges property.
65 	 */
66 	i = sizeof (px_p->px_bus_range);
67 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
68 	    "bus-range", (caddr_t)&px_p->px_bus_range, &i) != DDI_SUCCESS) {
69 		cmn_err(CE_WARN, "%s%d: no bus-range property\n",
70 		    ddi_driver_name(dip), ddi_get_instance(dip));
71 		return (DDI_FAILURE);
72 	}
73 	DBG(DBG_ATTACH, dip, "get_px_properties: bus-range (%x,%x)\n",
74 		px_p->px_bus_range.lo, px_p->px_bus_range.hi);
75 
76 	/*
77 	 * Get the interrupts property.
78 	 */
79 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
80 		"interrupts", (caddr_t)&px_p->px_inos,
81 		&px_p->px_inos_len) != DDI_SUCCESS) {
82 
83 		cmn_err(CE_WARN, "%s%d: no interrupts property\n",
84 			ddi_driver_name(dip), ddi_get_instance(dip));
85 		return (DDI_FAILURE);
86 	}
87 
88 	/*
89 	 * figure out number of interrupts in the "interrupts" property
90 	 * and convert them all into ino.
91 	 */
92 	i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1);
93 	i = CELLS_1275_TO_BYTES(i);
94 	no_of_intrs = px_p->px_inos_len / i;
95 	for (i = 0; i < no_of_intrs; i++)
96 		px_p->px_inos[i] = px_p->px_inos[i] & 0x3F;
97 
98 	/*
99 	 * Get the ranges property.
100 	 */
101 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
102 		(caddr_t)&px_p->px_ranges_p, &px_p->px_ranges_length) !=
103 		DDI_SUCCESS) {
104 
105 		cmn_err(CE_WARN, "%s%d: no ranges property\n",
106 			ddi_driver_name(dip), ddi_get_instance(dip));
107 		kmem_free(px_p->px_inos, px_p->px_inos_len);
108 		return (DDI_FAILURE);
109 	}
110 
111 	return (DDI_SUCCESS);
112 }
113 
114 /*
115  * px_free_props:
116  *
117  * This routine frees the memory used to cache the "interrupts"
118  * and "ranges" properties of the pci bus device node.
119  *
120  * used by: px_detach()
121  *
122  * return value: none
123  */
124 void
125 px_free_props(px_t *px_p)
126 {
127 	kmem_free(px_p->px_inos, px_p->px_inos_len);
128 	kmem_free(px_p->px_ranges_p, px_p->px_ranges_length);
129 }
130 
131 /*
132  * px_reloc_reg
133  *
134  * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses"
135  * property to fetch corresponding relocated address.
136  *
137  * used by: px_map()
138  *
139  * return value:
140  *
141  *	DDI_SUCCESS		- on success
142  *	DDI_ME_INVAL		- regspec is invalid
143  */
144 int
145 px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p,
146 	pci_regspec_t *rp)
147 {
148 	int assign_len, assign_entries, i;
149 	pci_regspec_t *assign_p;
150 	uint32_t phys_hi = rp->pci_phys_hi;
151 	uint32_t space_type = phys_hi & PCI_REG_ADDR_M;	/* 28-bit */
152 
153 	DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n",
154 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
155 		rp->pci_size_hi, rp->pci_size_low);
156 
157 	if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B)
158 		return (DDI_SUCCESS);
159 
160 	/*
161 	 * Hot plug will be taken care of later
162 	 * if (px_p->hotplug_capable == B_FALSE)
163 	 */
164 	{
165 		uint32_t bus = PCI_REG_BUS_G(phys_hi);
166 		if (bus < px_p->px_bus_range.lo ||
167 		    bus > px_p->px_bus_range.hi) {
168 			DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus);
169 			return (DDI_ME_INVAL);
170 		}
171 	}
172 
173 	i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
174 		"assigned-addresses", (caddr_t)&assign_p, &assign_len);
175 	if (i) {
176 		DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n",
177 			ddi_driver_name(rdip), ddi_get_instance(rdip), i);
178 		return (DDI_ME_INVAL);
179 	}
180 
181 	assign_entries = assign_len / sizeof (pci_regspec_t);
182 	for (i = 0; i < assign_entries; i++, assign_p++) {
183 		uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M;
184 		uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi);
185 
186 		if (PCI_REG_BDFR_G(phys_hi) != assign_addr)
187 			continue;
188 		if (space_type == assign_type) { /* exact match */
189 			rp->pci_phys_low += assign_p->pci_phys_low;
190 			break;
191 		}
192 		if (space_type == PCI_ADDR_MEM64 &&
193 			assign_type == PCI_ADDR_MEM32) {
194 			rp->pci_phys_low += assign_p->pci_phys_low;
195 			rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32;
196 			break;
197 		}
198 	}
199 	kmem_free(assign_p - i, assign_len);
200 	DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n",
201 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
202 		rp->pci_size_hi, rp->pci_size_low, i);
203 	return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
204 }
205 
206 int
207 px_search_ranges(px_t *px_p, uint32_t space_type, uint32_t reg_begin,
208     uint32_t reg_end, px_ranges_t **sel_rng_p, uint_t *base_offset_p)
209 {
210 	px_ranges_t *rng_p = px_p->px_ranges_p;
211 	int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t);
212 	int n;
213 	uint32_t rng_begin, rng_end;
214 
215 	for (n = 0; n < rng_n; n++, rng_p++) {
216 		if (space_type != PCI_REG_ADDR_G(rng_p->child_high))
217 			continue;	/* not the same space type */
218 
219 		rng_begin = rng_p->child_low;
220 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
221 			rng_begin += rng_p->child_high;
222 
223 		rng_end = rng_begin + rng_p->size_low - 1;
224 		if (reg_begin >= rng_begin && reg_end <= rng_end)
225 			break;
226 	}
227 	if (n >= rng_n)
228 		return (DDI_ME_REGSPEC_RANGE);
229 
230 	*base_offset_p = reg_begin - rng_begin + rng_p->parent_low;
231 	*sel_rng_p = rng_p;
232 
233 	return (DDI_SUCCESS);
234 }
235 
236 /*
237  * use "ranges" to translate relocated pci regspec into parent space
238  */
239 int
240 px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp)
241 {
242 	uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi);
243 	uint32_t reg_end, reg_begin = px_rp->pci_phys_low;
244 	uint32_t sz = px_rp->pci_size_low;
245 	px_ranges_t *sel_rng_p;
246 	int rval;
247 
248 	if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
249 		if (reg_begin > PCI_CONF_HDR_SIZE)
250 			return (DDI_ME_INVAL);
251 		sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
252 		reg_begin += px_rp->pci_phys_hi << 4;
253 	}
254 	reg_end = reg_begin + sz - 1;
255 
256 	if ((rval = px_search_ranges(px_p, space_type, reg_begin, reg_end,
257 	    &sel_rng_p, &new_rp->regspec_addr)) == DDI_SUCCESS) {
258 
259 		new_rp->regspec_bustype = sel_rng_p->parent_high;
260 		new_rp->regspec_size = sz;
261 
262 		DBG(DBG_MAP | DBG_CONT, px_p->px_dip,
263 		    "\tpx_xlate_reg: new_rp %x.%x %x\n",
264 		    new_rp->regspec_bustype, new_rp->regspec_addr, sz);
265 	}
266 
267 	return (rval);
268 }
269 
270 /*
271  * px_report_dev
272  *
273  * This function is called from our control ops routine on a
274  * DDI_CTLOPS_REPORTDEV request.
275  *
276  * The display format is
277  *
278  *	<name><inst> at <pname><pinst> device <dev> function <func>
279  *
280  * where
281  *
282  *	<name>		this device's name property
283  *	<inst>		this device's instance number
284  *	<name>		parent device's name property
285  *	<inst>		parent device's instance number
286  *	<dev>		this device's device number
287  *	<func>		this device's function number
288  */
289 int
290 px_report_dev(dev_info_t *dip)
291 {
292 	if (dip == (dev_info_t *)0)
293 		return (DDI_FAILURE);
294 	cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n",
295 	    ddi_node_name(dip), ddi_get_name_addr(dip),
296 	    ddi_driver_name(dip),
297 	    ddi_get_instance(dip));
298 	return (DDI_SUCCESS);
299 }
300 
301 
302 /*
303  * reg property for pcimem nodes that covers the entire address
304  * space for the node:  config, io, or memory.
305  */
306 pci_regspec_t pci_pcimem_reg[3] =
307 {
308 	{PCI_ADDR_CONFIG,			0, 0, 0, 0x800000	},
309 	{(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B),	0, 0, 0, PX_IO_SIZE	},
310 	{(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE	}
311 };
312 
313 /*
314  * px_name_child
315  *
316  * This function is called from init_child to name a node. It is
317  * also passed as a callback for node merging functions.
318  *
319  * return value: DDI_SUCCESS, DDI_FAILURE
320  */
321 static int
322 px_name_child(dev_info_t *child, char *name, int namelen)
323 {
324 	pci_regspec_t *pci_rp;
325 	int reglen;
326 	uint_t func;
327 	char **unit_addr;
328 	uint_t n;
329 
330 	/*
331 	 * Set the address portion of the node name based on
332 	 * unit-address property, if it exists.
333 	 * The interpretation of the unit-address is DD[,F]
334 	 * where DD is the device id and F is the function.
335 	 */
336 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
337 	    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
338 	    DDI_PROP_SUCCESS) {
339 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
340 			cmn_err(CE_WARN, "unit-address property in %s.conf"
341 			    " not well-formed", ddi_driver_name(child));
342 			ddi_prop_free(unit_addr);
343 			return (DDI_FAILURE);
344 		}
345 		(void) snprintf(name, namelen, "%s", *unit_addr);
346 		ddi_prop_free(unit_addr);
347 		return (DDI_SUCCESS);
348 	}
349 
350 	/*
351 	 * The unit-address property is does not exist. Set the address
352 	 * portion of the node name based on the function and device number.
353 	 */
354 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
355 	    "reg", (int **)&pci_rp, (uint_t *)&reglen) == DDI_SUCCESS) {
356 		if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
357 			cmn_err(CE_WARN, "reg property not well-formed");
358 			return (DDI_FAILURE);
359 		}
360 
361 		func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
362 		if (func != 0)
363 			(void) snprintf(name, namelen, "%x,%x",
364 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func);
365 		else
366 			(void) snprintf(name, namelen, "%x",
367 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi));
368 		ddi_prop_free(pci_rp);
369 		return (DDI_SUCCESS);
370 	}
371 
372 	cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
373 	return (DDI_FAILURE);
374 }
375 
376 int
377 px_uninit_child(px_t *px_p, dev_info_t *child)
378 {
379 	DBG(DBG_INIT_CLD, px_p->px_dip,
380 	    "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
381 	    ddi_driver_name(child), ddi_get_instance(child));
382 
383 	ddi_set_name_addr(child, NULL);
384 	ddi_remove_minor_node(child, NULL);
385 	impl_rem_dev_props(child);
386 
387 	DBG(DBG_PWR, ddi_get_parent(child), "\n\n");
388 
389 	pcie_uninitchild(child);
390 
391 	return (DDI_SUCCESS);
392 }
393 
394 /*
395  * px_init_child
396  *
397  * This function is called from our control ops routine on a
398  * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
399  * parent private data area.
400  *
401  * used by: pci_ctlops()
402  *
403  * return value: none
404  */
405 int
406 px_init_child(px_t *px_p, dev_info_t *child)
407 {
408 	dev_info_t	*parent_dip = px_p->px_dip;
409 	pci_regspec_t	*pci_rp;
410 	char		name[10];
411 	int		i, no_config;
412 
413 	/*
414 	 * The following is a special case for pcimem nodes.
415 	 * For these nodes we create a reg property with a
416 	 * single entry that covers the entire address space
417 	 * for the node (config, io or memory).
418 	 */
419 	if (strcmp(ddi_driver_name(child), "pcimem") == 0) {
420 		(void) ddi_prop_create(DDI_DEV_T_NONE, child,
421 		    DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg,
422 		    sizeof (pci_pcimem_reg));
423 		ddi_set_name_addr(child, "0");
424 		ddi_set_parent_data(child, NULL);
425 		return (DDI_SUCCESS);
426 	}
427 
428 	/*
429 	 * Check whether the node has config space or is a hard decode
430 	 * node (possibly created by a driver.conf file).
431 	 */
432 	no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
433 	    "no-config", 0);
434 
435 	/*
436 	 * Pseudo nodes indicate a prototype node with per-instance
437 	 * properties to be merged into the real h/w device node.
438 	 * However, do not merge if the no-config property is set
439 	 * (see PSARC 2000/088).
440 	 */
441 	if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) {
442 		extern int pci_allow_pseudo_children;
443 
444 		if (ddi_getlongprop(DDI_DEV_T_ANY, child,
445 		    DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) ==
446 		    DDI_SUCCESS) {
447 			cmn_err(CE_WARN, "cannot merge prototype from %s.conf",
448 			    ddi_driver_name(child));
449 			kmem_free(pci_rp, i);
450 			return (DDI_NOT_WELL_FORMED);
451 		}
452 		/*
453 		 * Name the child
454 		 */
455 		if (px_name_child(child, name, 10) != DDI_SUCCESS)
456 			return (DDI_FAILURE);
457 
458 		ddi_set_name_addr(child, name);
459 		ddi_set_parent_data(child, NULL);
460 
461 		/*
462 		 * Try to merge the properties from this prototype
463 		 * node into real h/w nodes.
464 		 */
465 		if (ndi_merge_node(child, px_name_child) == DDI_SUCCESS) {
466 			/*
467 			 * Merged ok - return failure to remove the node.
468 			 */
469 			ddi_set_name_addr(child, NULL);
470 			return (DDI_FAILURE);
471 		}
472 
473 		/* workaround for ddivs to run under PCI */
474 		if (pci_allow_pseudo_children)
475 			return (DDI_SUCCESS);
476 
477 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
478 		    ddi_driver_name(child), ddi_get_name_addr(child),
479 		    ddi_driver_name(child));
480 		ddi_set_name_addr(child, NULL);
481 		return (DDI_NOT_WELL_FORMED);
482 	}
483 
484 	if (px_name_child(child, name, 10) != DDI_SUCCESS)
485 		return (DDI_FAILURE);
486 	ddi_set_name_addr(child, name);
487 
488 	if (no_config != 0) {
489 		/*
490 		 * There is no config space so there's nothing more to do.
491 		 */
492 		return (DDI_SUCCESS);
493 	}
494 
495 	if (pcie_pm_hold(parent_dip) != DDI_SUCCESS) {
496 		DBG(DBG_PWR, parent_dip,
497 		    "INITCHILD: px_pm_hold failed\n");
498 		return (DDI_FAILURE);
499 	}
500 	/* Any return of DDI_FAILURE after this must call px_pm_release */
501 
502 	/*
503 	 * If configuration registers were previously saved by
504 	 * child (before it went to D3), then let the child do the
505 	 * restore to set up the config regs as it'll first need to
506 	 * power the device out of D3.
507 	 */
508 	if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
509 	    "config-regs-saved-by-child") == 1) {
510 		DBG(DBG_PWR, child,
511 		    "INITCHILD: config regs to be restored by child\n");
512 
513 		return (DDI_SUCCESS);
514 	}
515 
516 	DBG(DBG_PWR, parent_dip,
517 	    "INITCHILD: config regs setup for %s@%s\n",
518 	    ddi_node_name(child), ddi_get_name_addr(child));
519 
520 	pcie_initchild(child);
521 
522 	/*
523 	 * Handle chip specific init-child tasks.
524 	 */
525 	pcie_pm_release(parent_dip);
526 
527 	return (DDI_SUCCESS);
528 }
529 
530 /*
531  * px_get_reg_set_size
532  *
533  * Given a dev info pointer to a pci child and a register number, this
534  * routine returns the size element of that reg set property.
535  *
536  * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE
537  *
538  * return value: size of reg set on success, 0 on error
539  */
540 off_t
541 px_get_reg_set_size(dev_info_t *child, int rnumber)
542 {
543 	pci_regspec_t *pci_rp;
544 	off_t size = 0;
545 	int i;
546 
547 	if (rnumber < 0)
548 		return (0);
549 
550 	/*
551 	 * Get the reg property for the device.
552 	 */
553 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
554 	    (caddr_t)&pci_rp, &i) != DDI_SUCCESS)
555 		return (0);
556 
557 	if (rnumber >= (i / (int)sizeof (pci_regspec_t)))
558 		goto done;
559 
560 	size = pci_rp[rnumber].pci_size_low |
561 		((uint64_t)pci_rp[rnumber].pci_size_hi << 32);
562 done:
563 	kmem_free(pci_rp, i);
564 	return (size);
565 }
566 
567 
568 /*
569  * px_get_nreg_set
570  *
571  * Given a dev info pointer to a pci child, this routine returns the
572  * number of sets in its "reg" property.
573  *
574  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
575  *
576  * return value: # of reg sets on success, zero on error
577  */
578 uint_t
579 px_get_nreg_set(dev_info_t *child)
580 {
581 	pci_regspec_t *pci_rp;
582 	int i, n;
583 
584 	/*
585 	 * Get the reg property for the device.
586 	 */
587 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
588 	    (caddr_t)&pci_rp, &i) != DDI_SUCCESS)
589 		return (0);
590 
591 	n = i / (int)sizeof (pci_regspec_t);
592 	kmem_free(pci_rp, i);
593 	return (n);
594 }
595 
596 
597 /*
598  * px_get_nintr
599  *
600  * Given a dev info pointer to a pci child, this routine returns the
601  * number of items in its "interrupts" property.
602  *
603  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
604  *
605  * return value: # of interrupts on success, zero on error
606  */
607 uint_t
608 px_get_nintr(dev_info_t *child)
609 {
610 	int *pci_ip;
611 	int i, n;
612 
613 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
614 	    "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS)
615 		return (0);
616 
617 	n = i / (int)sizeof (uint_t);
618 	kmem_free(pci_ip, i);
619 	return (n);
620 }
621 
622 uint64_t
623 px_get_cfg_pabase(px_t *px_p)
624 {
625 	int i;
626 	px_ranges_t *rangep = px_p->px_ranges_p;
627 	int nrange = px_p->px_ranges_length / sizeof (px_ranges_t);
628 	uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
629 
630 	ASSERT(cfg_space_type == 0);
631 
632 	for (i = 0; i < nrange; i++, rangep++) {
633 		if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type)
634 			break;
635 	}
636 
637 	if (i >= nrange)
638 		cmn_err(CE_PANIC, "no cfg space in px(%p) ranges prop.\n",
639 			px_p);
640 
641 	return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low);
642 }
643 
644 /*
645  * decodes standard PCI config space 16bit error status reg
646  */
647 int
648 px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg)
649 {
650 	int nerr = ddi_get_instance(dip); /* temp for instance */
651 	uint64_t perr_fatal = px_perr_fatal & (1 << nerr);
652 	uint64_t serr_fatal = px_serr_fatal & (1 << nerr);
653 	nerr = 0;
654 
655 	if ((status_reg & PCI_STAT_PERROR) && perr_fatal)
656 		nerr++;
657 	if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal)
658 		nerr++;
659 	if (status_reg & PCI_STAT_R_MAST_AB)
660 		nerr++;
661 	if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal)
662 		nerr++;
663 
664 	cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b",
665 	    ddi_driver_name(dip), ddi_get_instance(dip), err_msg,
666 	    (uint32_t)status_reg, PX_STATUS_BITS);
667 
668 	return (nerr);
669 }
670 
671 /*
672  * This is a software workaround to fix the Broadcom PCIe-PCI bridge
673  * prefetch bug. Existence of a "cross-page-prefetch" property in the
674  * child or parent node means the px nexus driver has to allocate an
675  * extra page and make it valid one, for any DVMA request that comes
676  * from any of the Broadcom child device.
677  */
678 boolean_t
679 px_child_prefetch(dev_info_t *child)
680 {
681 	if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_NOTPROM,
682 	    "cross-page-prefetch"))
683 		return (B_TRUE);
684 
685 	return (B_FALSE);
686 }
687