xref: /titanic_44/usr/src/uts/sun4/io/px/px_util.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * PCI nexus utility routines:
31  *	property and config routines for attach()
32  *	reg/intr/range/assigned-address property routines for bus_map()
33  *	init_child()
34  *	fault handling
35  */
36 
37 #include <sys/types.h>
38 #include <sys/kmem.h>
39 #include <sys/async.h>
40 #include <sys/sysmacros.h>
41 #include <sys/sunddi.h>
42 #include <sys/sunndi.h>
43 #include <sys/ddi_impldefs.h>
44 #include "px_obj.h"
45 #include "pcie_pwr.h"
46 #include <px_regs.h>
47 #include <px_csr.h>
48 
49 /*LINTLIBRARY*/
50 
51 /*
52  * px_get_props
53  *
54  * This function is called from the attach routine to get the key
55  * properties of the pci nodes.
56  *
57  * used by: px_attach()
58  *
59  * return value: DDI_FAILURE on failure
60  */
61 int
62 px_get_props(px_t *px_p, dev_info_t *dip)
63 {
64 	int i, no_of_intrs;
65 
66 	/*
67 	 * Get the bus-ranges property.
68 	 */
69 	i = sizeof (px_p->px_bus_range);
70 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
71 	    "bus-range", (caddr_t)&px_p->px_bus_range, &i) != DDI_SUCCESS) {
72 		cmn_err(CE_WARN, "%s%d: no bus-range property\n",
73 		    ddi_driver_name(dip), ddi_get_instance(dip));
74 		return (DDI_FAILURE);
75 	}
76 	DBG(DBG_ATTACH, dip, "get_px_properties: bus-range (%x,%x)\n",
77 		px_p->px_bus_range.lo, px_p->px_bus_range.hi);
78 
79 	/*
80 	 * Get the interrupts property.
81 	 */
82 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
83 		"interrupts", (caddr_t)&px_p->px_inos,
84 		&px_p->px_inos_len) != DDI_SUCCESS) {
85 
86 		cmn_err(CE_WARN, "%s%d: no interrupts property\n",
87 			ddi_driver_name(dip), ddi_get_instance(dip));
88 		return (DDI_FAILURE);
89 	}
90 
91 	/*
92 	 * figure out number of interrupts in the "interrupts" property
93 	 * and convert them all into ino.
94 	 */
95 	i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1);
96 	i = CELLS_1275_TO_BYTES(i);
97 	no_of_intrs = px_p->px_inos_len / i;
98 	for (i = 0; i < no_of_intrs; i++)
99 		px_p->px_inos[i] = px_p->px_inos[i] & 0x3F;
100 
101 	/*
102 	 * Get the ranges property.
103 	 */
104 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
105 		(caddr_t)&px_p->px_ranges_p, &px_p->px_ranges_length) !=
106 		DDI_SUCCESS) {
107 
108 		cmn_err(CE_WARN, "%s%d: no ranges property\n",
109 			ddi_driver_name(dip), ddi_get_instance(dip));
110 		kmem_free(px_p->px_inos, px_p->px_inos_len);
111 		return (DDI_FAILURE);
112 	}
113 
114 	px_p->px_thermal_interrupt =
115 		ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
116 				"thermal-interrupt", -1);
117 	DBG(DBG_ATTACH, dip, "get_px_properties: thermal_interrupt=%d\n",
118 	    px_p->px_thermal_interrupt);
119 	return (DDI_SUCCESS);
120 }
121 
122 /*
123  * px_free_props:
124  *
125  * This routine frees the memory used to cache the "interrupts"
126  * and "ranges" properties of the pci bus device node.
127  *
128  * used by: px_detach()
129  *
130  * return value: none
131  */
132 void
133 px_free_props(px_t *px_p)
134 {
135 	kmem_free(px_p->px_inos, px_p->px_inos_len);
136 	kmem_free(px_p->px_ranges_p, px_p->px_ranges_length);
137 }
138 
139 /*
140  * px_reloc_reg
141  *
142  * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses"
143  * property to fetch corresponding relocated address.
144  *
145  * used by: px_map()
146  *
147  * return value:
148  *
149  *	DDI_SUCCESS		- on success
150  *	DDI_ME_INVAL		- regspec is invalid
151  */
152 int
153 px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p,
154 	pci_regspec_t *rp)
155 {
156 	int assign_len, assign_entries, i;
157 	pci_regspec_t *assign_p;
158 	uint32_t phys_hi = rp->pci_phys_hi;
159 	uint32_t space_type = phys_hi & PCI_REG_ADDR_M;	/* 28-bit */
160 
161 	DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n",
162 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
163 		rp->pci_size_hi, rp->pci_size_low);
164 
165 	if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B)
166 		return (DDI_SUCCESS);
167 
168 	/*
169 	 * Hot plug will be taken care of later
170 	 * if (px_p->hotplug_capable == B_FALSE)
171 	 */
172 	{
173 		uint32_t bus = PCI_REG_BUS_G(phys_hi);
174 		if (bus < px_p->px_bus_range.lo ||
175 		    bus > px_p->px_bus_range.hi) {
176 			DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus);
177 			return (DDI_ME_INVAL);
178 		}
179 	}
180 
181 	i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
182 		"assigned-addresses", (caddr_t)&assign_p, &assign_len);
183 	if (i) {
184 		DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n",
185 			ddi_driver_name(rdip), ddi_get_instance(rdip), i);
186 		return (DDI_ME_INVAL);
187 	}
188 
189 	assign_entries = assign_len / sizeof (pci_regspec_t);
190 	for (i = 0; i < assign_entries; i++, assign_p++) {
191 		uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M;
192 		uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi);
193 
194 		if (PCI_REG_BDFR_G(phys_hi) != assign_addr)
195 			continue;
196 		if (space_type == assign_type) { /* exact match */
197 			rp->pci_phys_low += assign_p->pci_phys_low;
198 			break;
199 		}
200 		if (space_type == PCI_ADDR_MEM64 &&
201 			assign_type == PCI_ADDR_MEM32) {
202 			rp->pci_phys_low += assign_p->pci_phys_low;
203 			rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32;
204 			break;
205 		}
206 	}
207 	kmem_free(assign_p - i, assign_len);
208 	DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n",
209 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
210 		rp->pci_size_hi, rp->pci_size_low, i);
211 	return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
212 }
213 
214 /*
215  * use "ranges" to translate relocated pci regspec into parent space
216  */
217 int
218 px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp)
219 {
220 	int n;
221 	px_ranges_t *rng_p = px_p->px_ranges_p;
222 	int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t);
223 
224 	uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi);
225 	uint32_t reg_end, reg_begin = px_rp->pci_phys_low;
226 	uint32_t sz = px_rp->pci_size_low;
227 
228 	uint32_t rng_begin, rng_end;
229 
230 	if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
231 		if (reg_begin > PCI_CONF_HDR_SIZE)
232 			return (DDI_ME_INVAL);
233 		sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
234 		reg_begin += px_rp->pci_phys_hi << 4;
235 	}
236 	reg_end = reg_begin + sz - 1;
237 
238 	for (n = 0; n < rng_n; n++, rng_p++) {
239 		if (space_type != PCI_REG_ADDR_G(rng_p->child_high))
240 			continue;	/* not the same space type */
241 
242 		rng_begin = rng_p->child_low;
243 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
244 			rng_begin += rng_p->child_high;
245 
246 		rng_end = rng_begin + rng_p->size_low - 1;
247 		if (reg_begin >= rng_begin && reg_end <= rng_end)
248 			break;
249 	}
250 	if (n >= rng_n)
251 		return (DDI_ME_REGSPEC_RANGE);
252 
253 	new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
254 	new_rp->regspec_bustype = rng_p->parent_high;
255 	new_rp->regspec_size = sz;
256 	DBG(DBG_MAP | DBG_CONT, px_p->px_dip,
257 		"\tpx_xlate_reg: entry %d new_rp %x.%x %x\n",
258 		n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
259 
260 	return (DDI_SUCCESS);
261 }
262 
263 /*
264  * px_map_registers
265  *
266  * This function is called from the attach routine to map the registers
267  * accessed by this driver.
268  *
269  * used by: px_attach()
270  *
271  * return value: DDI_FAILURE on failure
272  *
273  * Remove px_map_regs() from here and move them to SUN4U library code,
274  * after complete virtualization (after porting MSI and Error handling code).
275  */
276 int
277 px_map_regs(px_t *px_p, dev_info_t *dip)
278 {
279 	ddi_device_acc_attr_t	attr;
280 	px_reg_bank_t		reg_bank = PX_REG_CSR;
281 
282 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
283 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
284 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
285 
286 	/*
287 	 * PCI CSR Base
288 	 */
289 	if (ddi_regs_map_setup(dip, reg_bank, &px_p->px_address[reg_bank],
290 	    0, 0, &attr, &px_p->px_ac[reg_bank]) != DDI_SUCCESS) {
291 		goto fail;
292 	}
293 
294 	reg_bank++;
295 
296 	/*
297 	 * XBUS CSR Base
298 	 */
299 	if (ddi_regs_map_setup(dip, reg_bank, &px_p->px_address[reg_bank],
300 	    0, 0, &attr, &px_p->px_ac[reg_bank]) != DDI_SUCCESS) {
301 		goto fail;
302 	}
303 
304 	px_p->px_address[reg_bank] -= FIRE_CONTROL_STATUS;
305 
306 done:
307 	for (; reg_bank >= PX_REG_CSR; reg_bank--) {
308 		DBG(DBG_ATTACH, dip, "reg_bank 0x%x address 0x%p\n",
309 		    reg_bank, px_p->px_address[reg_bank]);
310 	}
311 
312 	return (DDI_SUCCESS);
313 
314 fail:
315 	cmn_err(CE_WARN, "%s%d: unable to map reg entry %d\n",
316 	    ddi_driver_name(dip), ddi_get_instance(dip), reg_bank);
317 
318 	for (reg_bank--; reg_bank >= PX_REG_CSR; reg_bank--) {
319 		px_p->px_address[reg_bank] = NULL;
320 		ddi_regs_map_free(&px_p->px_ac[reg_bank]);
321 	}
322 
323 	return (DDI_FAILURE);
324 }
325 
326 /*
327  * px_unmap_regs:
328  *
329  * This routine unmap the registers mapped by map_px_registers.
330  *
331  * used by: px_detach(), and error conditions in px_attach()
332  *
333  * return value: none
334  */
335 void
336 px_unmap_regs(px_t *px_p)
337 {
338 	int i;
339 
340 	for (i = 0; i < 4; i++) {
341 		if (px_p->px_ac[i])
342 			ddi_regs_map_free(&px_p->px_ac[i]);
343 	}
344 }
345 
346 /*
347  * px_report_dev
348  *
349  * This function is called from our control ops routine on a
350  * DDI_CTLOPS_REPORTDEV request.
351  *
352  * The display format is
353  *
354  *	<name><inst> at <pname><pinst> device <dev> function <func>
355  *
356  * where
357  *
358  *	<name>		this device's name property
359  *	<inst>		this device's instance number
360  *	<name>		parent device's name property
361  *	<inst>		parent device's instance number
362  *	<dev>		this device's device number
363  *	<func>		this device's function number
364  */
365 int
366 px_report_dev(dev_info_t *dip)
367 {
368 	if (dip == (dev_info_t *)0)
369 		return (DDI_FAILURE);
370 	cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n",
371 	    ddi_node_name(dip), ddi_get_name_addr(dip),
372 	    ddi_driver_name(dip),
373 	    ddi_get_instance(dip));
374 	return (DDI_SUCCESS);
375 }
376 
377 
378 /*
379  * reg property for pcimem nodes that covers the entire address
380  * space for the node:  config, io, or memory.
381  */
382 pci_regspec_t pci_pcimem_reg[3] =
383 {
384 	{PCI_ADDR_CONFIG,			0, 0, 0, 0x800000	},
385 	{(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B),	0, 0, 0, PX_IO_SIZE	},
386 	{(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE	}
387 };
388 
389 /*
390  * px_name_child
391  *
392  * This function is called from init_child to name a node. It is
393  * also passed as a callback for node merging functions.
394  *
395  * return value: DDI_SUCCESS, DDI_FAILURE
396  */
397 static int
398 px_name_child(dev_info_t *child, char *name, int namelen)
399 {
400 	pci_regspec_t *pci_rp;
401 	int reglen;
402 	uint_t func;
403 	char **unit_addr;
404 	uint_t n;
405 
406 	/*
407 	 * Set the address portion of the node name based on
408 	 * unit-address property, if it exists.
409 	 * The interpretation of the unit-address is DD[,F]
410 	 * where DD is the device id and F is the function.
411 	 */
412 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
413 	    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
414 	    DDI_PROP_SUCCESS) {
415 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
416 			cmn_err(CE_WARN, "unit-address property in %s.conf"
417 			    " not well-formed", ddi_driver_name(child));
418 			ddi_prop_free(unit_addr);
419 			return (DDI_FAILURE);
420 		}
421 		(void) snprintf(name, namelen, "%s", *unit_addr);
422 		ddi_prop_free(unit_addr);
423 		return (DDI_SUCCESS);
424 	}
425 
426 	/*
427 	 * The unit-address property is does not exist. Set the address
428 	 * portion of the node name based on the function and device number.
429 	 */
430 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
431 	    "reg", (int **)&pci_rp, (uint_t *)&reglen) == DDI_SUCCESS) {
432 		if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
433 			cmn_err(CE_WARN, "reg property not well-formed");
434 			return (DDI_FAILURE);
435 		}
436 
437 		func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
438 		if (func != 0)
439 			(void) snprintf(name, namelen, "%x,%x",
440 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func);
441 		else
442 			(void) snprintf(name, namelen, "%x",
443 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi));
444 		ddi_prop_free(pci_rp);
445 		return (DDI_SUCCESS);
446 	}
447 
448 	cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
449 	return (DDI_FAILURE);
450 }
451 
452 int
453 px_uninit_child(px_t *px_p, dev_info_t *child)
454 {
455 	DBG(DBG_INIT_CLD, px_p->px_dip,
456 	    "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
457 	    ddi_driver_name(child), ddi_get_instance(child));
458 
459 	ddi_set_name_addr(child, NULL);
460 	ddi_remove_minor_node(child, NULL);
461 	impl_rem_dev_props(child);
462 	return (DDI_SUCCESS);
463 }
464 
465 /*ARGSUSED*/
466 void
467 px_post_init_child(px_t *px_p, dev_info_t *child)
468 {
469 	/* Add px specific PEC code */
470 }
471 
472 /*
473  * px_init_child
474  *
475  * This function is called from our control ops routine on a
476  * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
477  * parent private data area.
478  *
479  * used by: pci_ctlops()
480  *
481  * return value: none
482  */
483 int
484 px_init_child(px_t *px_p, dev_info_t *child)
485 {
486 	pci_regspec_t *pci_rp;
487 	char name[10];
488 	ddi_acc_handle_t config_handle;
489 	uint16_t command_preserve, command;
490 	uint8_t bcr;
491 	uint8_t header_type, min_gnt;
492 	uint16_t latency_timer;
493 	uint_t n;
494 	int i, no_config;
495 
496 	/*
497 	 * The following is a special case for pcimem nodes.
498 	 * For these nodes we create a reg property with a
499 	 * single entry that covers the entire address space
500 	 * for the node (config, io or memory).
501 	 */
502 	if (strcmp(ddi_driver_name(child), "pcimem") == 0) {
503 		(void) ddi_prop_create(DDI_DEV_T_NONE, child,
504 		    DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg,
505 		    sizeof (pci_pcimem_reg));
506 		ddi_set_name_addr(child, "0");
507 		ddi_set_parent_data(child, NULL);
508 		return (DDI_SUCCESS);
509 	}
510 
511 	/*
512 	 * Check whether the node has config space or is a hard decode
513 	 * node (possibly created by a driver.conf file).
514 	 */
515 	no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
516 	    "no-config", 0);
517 
518 	/*
519 	 * Pseudo nodes indicate a prototype node with per-instance
520 	 * properties to be merged into the real h/w device node.
521 	 * However, do not merge if the no-config property is set
522 	 * (see PSARC 2000/088).
523 	 */
524 	if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) {
525 		extern int pci_allow_pseudo_children;
526 
527 		if (ddi_getlongprop(DDI_DEV_T_ANY, child,
528 		    DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) ==
529 		    DDI_SUCCESS) {
530 			cmn_err(CE_WARN, "cannot merge prototype from %s.conf",
531 			    ddi_driver_name(child));
532 			kmem_free(pci_rp, i);
533 			return (DDI_NOT_WELL_FORMED);
534 		}
535 		/*
536 		 * Name the child
537 		 */
538 		if (px_name_child(child, name, 10) != DDI_SUCCESS)
539 			return (DDI_FAILURE);
540 
541 		ddi_set_name_addr(child, name);
542 		ddi_set_parent_data(child, NULL);
543 
544 		/*
545 		 * Try to merge the properties from this prototype
546 		 * node into real h/w nodes.
547 		 */
548 		if (ndi_merge_node(child, px_name_child) == DDI_SUCCESS) {
549 			/*
550 			 * Merged ok - return failure to remove the node.
551 			 */
552 			ddi_set_name_addr(child, NULL);
553 			return (DDI_FAILURE);
554 		}
555 
556 		/* workaround for ddivs to run under PCI */
557 		if (pci_allow_pseudo_children)
558 			return (DDI_SUCCESS);
559 
560 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
561 		    ddi_driver_name(child), ddi_get_name_addr(child),
562 		    ddi_driver_name(child));
563 		ddi_set_name_addr(child, NULL);
564 		return (DDI_NOT_WELL_FORMED);
565 	}
566 
567 	if (px_name_child(child, name, 10) != DDI_SUCCESS)
568 		return (DDI_FAILURE);
569 	ddi_set_name_addr(child, name);
570 
571 	if (no_config != 0) {
572 		/*
573 		 * There is no config space so there's nothing more to do.
574 		 */
575 		return (DDI_SUCCESS);
576 	}
577 
578 	if (pcie_pm_hold(px_p->px_dip) != DDI_SUCCESS) {
579 		DBG(DBG_PWR, px_p->px_dip,
580 		    "INITCHILD: px_pm_hold failed\n");
581 		return (DDI_FAILURE);
582 	}
583 	/* Any return of DDI_FAILURE after this must call px_pm_release */
584 
585 	/*
586 	 * If configuration registers were previously saved by
587 	 * child (before it went to D3), then let the child do the
588 	 * restore to set up the config regs as it'll first need to
589 	 * power the device out of D3.
590 	 */
591 	if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
592 	    "config-regs-saved-by-child") == 1) {
593 		DBG(DBG_PWR, child,
594 		    "INITCHILD: config regs to be restored by child\n");
595 
596 		return (DDI_SUCCESS);
597 	}
598 
599 	DBG(DBG_PWR, ddi_get_parent(child),
600 	    "INITCHILD: config regs setup for %s@%s\n",
601 	    ddi_node_name(child), ddi_get_name_addr(child));
602 
603 	/*
604 	 * Map the child configuration space to for initialization.
605 	 * We assume the obp will do the following in the devices
606 	 * config space:
607 	 *
608 	 *	Set the latency-timer register to values appropriate
609 	 *	for the devices on the bus (based on other devices
610 	 *	MIN_GNT and MAX_LAT registers.
611 	 *
612 	 *	Set the fast back-to-back enable bit in the command
613 	 *	register if it's supported and all devices on the bus
614 	 *	have the capability.
615 	 *
616 	 */
617 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
618 		ddi_set_name_addr(child, NULL);
619 		pcie_pm_release(px_p->px_dip);
620 		return (DDI_FAILURE);
621 	}
622 
623 	/*
624 	 * Determine the configuration header type.
625 	 */
626 	header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
627 	DBG(DBG_INIT_CLD, px_p->px_dip, "%s: header_type=%x\n",
628 	    ddi_driver_name(child), header_type);
629 
630 	/*
631 	 * Support for "command-preserve" property.  Note that we
632 	 * add PCI_COMM_BACK2BACK_ENAB to the bits to be preserved
633 	 * since the obp will set this if the device supports and
634 	 * all targets on the same bus support it.  Since psycho
635 	 * doesn't support PCI_COMM_BACK2BACK_ENAB, it will never
636 	 * be set.  This is just here in case future revs do support
637 	 * PCI_COMM_BACK2BACK_ENAB.
638 	 */
639 	command_preserve =
640 	    ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
641 		"command-preserve", 0);
642 	DBG(DBG_INIT_CLD, px_p->px_dip, "%s: command-preserve=%x\n",
643 	    ddi_driver_name(child), command_preserve);
644 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
645 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
646 	command |= (px_command_default & ~command_preserve);
647 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
648 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
649 	DBG(DBG_INIT_CLD, px_p->px_dip, "%s: command=%x\n",
650 	    ddi_driver_name(child),
651 	    pci_config_get16(config_handle, PCI_CONF_COMM));
652 
653 	/*
654 	 * If the device has a bus control register then program it
655 	 * based on the settings in the command register.
656 	 */
657 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
658 		bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
659 		if (px_command_default & PCI_COMM_PARITY_DETECT)
660 			bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
661 		if (px_command_default & PCI_COMM_SERR_ENABLE)
662 			bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
663 		bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
664 		pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
665 	}
666 
667 	/*
668 	 * Initialize latency timer registers if needed.
669 	 */
670 	if (px_set_latency_timer_register &&
671 	    ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
672 		"latency-timer", 0) == 0) {
673 
674 		latency_timer = px_latency_timer;
675 		if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
676 			pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER,
677 			    latency_timer);
678 		} else {
679 			min_gnt = pci_config_get8(config_handle,
680 			    PCI_CONF_MIN_G);
681 			DBG(DBG_INIT_CLD, px_p->px_dip, "%s: min_gnt=%x\n",
682 			    ddi_driver_name(child), min_gnt);
683 		}
684 		latency_timer = MIN(latency_timer, 0xff);
685 		pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER,
686 		    latency_timer);
687 		n = pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER);
688 		if (n != 0)
689 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, child,
690 			    "latency-timer", n);
691 	}
692 
693 	pci_config_teardown(&config_handle);
694 
695 	/*
696 	 * Handle chip specific init-child tasks.
697 	 */
698 	px_post_init_child(px_p, child);
699 	pcie_pm_release(px_p->px_dip);
700 
701 	return (DDI_SUCCESS);
702 }
703 
704 /*
705  * px_get_reg_set_size
706  *
707  * Given a dev info pointer to a pci child and a register number, this
708  * routine returns the size element of that reg set property.
709  *
710  * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE
711  *
712  * return value: size of reg set on success, -1 on error
713  */
714 off_t
715 px_get_reg_set_size(dev_info_t *child, int rnumber)
716 {
717 	pci_regspec_t *pci_rp;
718 	off_t size = -1;
719 	int i;
720 
721 	if (rnumber < 0)
722 		return (-1);
723 
724 	/*
725 	 * Get the reg property for the device.
726 	 */
727 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
728 	    (caddr_t)&pci_rp, &i) != DDI_SUCCESS)
729 		return (-1);
730 
731 	if (rnumber >= (i / (int)sizeof (pci_regspec_t)))
732 		goto done;
733 
734 	size = pci_rp[rnumber].pci_size_low |
735 		((uint64_t)pci_rp[rnumber].pci_size_hi << 32);
736 done:
737 	kmem_free(pci_rp, i);
738 	return (size);
739 }
740 
741 
742 /*
743  * px_get_nreg_set
744  *
745  * Given a dev info pointer to a pci child, this routine returns the
746  * number of sets in its "reg" property.
747  *
748  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
749  *
750  * return value: # of reg sets on success, zero on error
751  */
752 uint_t
753 px_get_nreg_set(dev_info_t *child)
754 {
755 	pci_regspec_t *pci_rp;
756 	int i, n;
757 
758 	/*
759 	 * Get the reg property for the device.
760 	 */
761 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
762 	    (caddr_t)&pci_rp, &i) != DDI_SUCCESS)
763 		return (0);
764 
765 	n = i / (int)sizeof (pci_regspec_t);
766 	kmem_free(pci_rp, i);
767 	return (n);
768 }
769 
770 
771 /*
772  * px_get_nintr
773  *
774  * Given a dev info pointer to a pci child, this routine returns the
775  * number of items in its "interrupts" property.
776  *
777  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
778  *
779  * return value: # of interrupts on success, zero on error
780  */
781 uint_t
782 px_get_nintr(dev_info_t *child)
783 {
784 	int *pci_ip;
785 	int i, n;
786 
787 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
788 	    "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS)
789 		return (0);
790 
791 	n = i / (int)sizeof (uint_t);
792 	kmem_free(pci_ip, i);
793 	return (n);
794 }
795 
796 uint64_t
797 px_get_cfg_pabase(px_t *px_p)
798 {
799 	int i;
800 	px_ranges_t *rangep = px_p->px_ranges_p;
801 	int nrange = px_p->px_ranges_length / sizeof (px_ranges_t);
802 	uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
803 
804 	ASSERT(cfg_space_type == 0);
805 
806 	for (i = 0; i < nrange; i++, rangep++) {
807 		if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type)
808 			break;
809 	}
810 
811 	if (i >= nrange)
812 		cmn_err(CE_PANIC, "no cfg space in px(%x) ranges prop.\n",
813 			(void *)px_p);
814 
815 	return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low);
816 }
817 
818 /*
819  * decodes standard PCI config space 16bit error status reg
820  */
821 int
822 px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg)
823 {
824 	int nerr = ddi_get_instance(dip); /* temp for instance */
825 	uint64_t perr_fatal = px_perr_fatal & (1 << nerr);
826 	uint64_t serr_fatal = px_serr_fatal & (1 << nerr);
827 	nerr = 0;
828 
829 	if ((status_reg & PCI_STAT_PERROR) && perr_fatal)
830 		nerr++;
831 	if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal)
832 		nerr++;
833 	if (status_reg & PCI_STAT_R_MAST_AB)
834 		nerr++;
835 	if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal)
836 		nerr++;
837 
838 	cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b",
839 	    ddi_driver_name(dip), ddi_get_instance(dip), err_msg,
840 	    (uint32_t)status_reg, PX_STATUS_BITS);
841 
842 	return (nerr);
843 }
844 
845 /*
846  * remove the following functions once we port error handling and other
847  * misc functionalities based on new VPCI interfaces.
848  */
849 uint64_t
850 px_get_err_reg(caddr_t csr, uint32_t off)
851 {
852 	return (CSR_XR(csr, off));
853 }
854 
855 void
856 px_set_err_reg(caddr_t csr, uint32_t off, uint64_t val)
857 {
858 	CSR_XS(csr, off, val);
859 }
860