xref: /illumos-gate/usr/src/uts/sun4u/opl/io/pcicmu/pcmu_util.c (revision 9b65801e8bd9a9007efed89ac479e99b9dab83d1)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * CMU-CH nexus utility routines:
29  *	property and config routines for attach()
30  *	reg/intr/range/assigned-address property routines for bus_map()
31  *	init_child()
32  *	fault handling
33  *	debug functions
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/fm/protocol.h>
43 #include <sys/fm/io/pci.h>
44 #include <sys/fm/util.h>
45 #include <sys/ddi_impldefs.h>
46 #include <sys/pcicmu/pcicmu.h>
47 #include <sys/promif.h>
48 
49 /*
50  * get_pcmu_properties
51  *
52  * This function is called from the attach routine to get the key
53  * properties of the pci nodes.
54  *
55  * used by: pcmu_attach()
56  *
57  * return value: DDI_FAILURE on failure
58  */
59 int
get_pcmu_properties(pcmu_t * pcmu_p,dev_info_t * dip)60 get_pcmu_properties(pcmu_t *pcmu_p, dev_info_t *dip)
61 {
62 	int i;
63 
64 	/*
65 	 * Get the device's port id.
66 	 */
67 	if ((pcmu_p->pcmu_id = (uint32_t)pcmu_get_portid(dip)) == -1u) {
68 		cmn_err(CE_WARN, "%s%d: no portid property\n",
69 		    ddi_driver_name(dip), ddi_get_instance(dip));
70 		return (DDI_FAILURE);
71 	}
72 
73 	/*
74 	 * Get the bus-ranges property.
75 	 */
76 	i = sizeof (pcmu_p->pcmu_bus_range);
77 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
78 	    "bus-range", (caddr_t)&pcmu_p->pcmu_bus_range, &i) != DDI_SUCCESS) {
79 		cmn_err(CE_WARN, "%s%d: no bus-range property\n",
80 		    ddi_driver_name(dip), ddi_get_instance(dip));
81 		return (DDI_FAILURE);
82 	}
83 	PCMU_DBG2(PCMU_DBG_ATTACH, dip,
84 	    "get_pcmu_properties: bus-range (%x,%x)\n",
85 	    pcmu_p->pcmu_bus_range.lo, pcmu_p->pcmu_bus_range.hi);
86 
87 	/*
88 	 * Get the ranges property.
89 	 */
90 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
91 	    (caddr_t)&pcmu_p->pcmu_ranges, &pcmu_p->pcmu_ranges_length) !=
92 	    DDI_SUCCESS) {
93 		cmn_err(CE_WARN, "%s%d: no ranges property\n",
94 		    ddi_driver_name(dip), ddi_get_instance(dip));
95 		return (DDI_FAILURE);
96 	}
97 	pcmu_fix_ranges(pcmu_p->pcmu_ranges,
98 	    pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t));
99 
100 	/*
101 	 * Determine the number upa slot interrupts.
102 	 */
103 	pcmu_p->pcmu_numproxy = pcmu_get_numproxy(pcmu_p->pcmu_dip);
104 	PCMU_DBG1(PCMU_DBG_ATTACH, dip, "get_pcmu_properties: numproxy=%d\n",
105 	    pcmu_p->pcmu_numproxy);
106 	return (DDI_SUCCESS);
107 }
108 
109 /*
110  * free_pcmu_properties:
111  *
112  * This routine frees the memory used to cache the
113  * "ranges" properties of the pci bus device node.
114  *
115  * used by: pcmu_detach()
116  *
117  * return value: none
118  */
119 void
free_pcmu_properties(pcmu_t * pcmu_p)120 free_pcmu_properties(pcmu_t *pcmu_p)
121 {
122 	kmem_free(pcmu_p->pcmu_ranges, pcmu_p->pcmu_ranges_length);
123 }
124 
125 /*
126  * pcmu_reloc_reg
127  *
128  * If the "reg" entry (*pcmu_rp) is relocatable, lookup "assigned-addresses"
129  * property to fetch corresponding relocated address.
130  *
131  * used by: pcmu_map()
132  *
133  * return value:
134  *
135  *	DDI_SUCCESS		- on success
136  *	DDI_ME_INVAL		- regspec is invalid
137  */
138 int
pcmu_reloc_reg(dev_info_t * dip,dev_info_t * rdip,pcmu_t * pcmu_p,pci_regspec_t * rp)139 pcmu_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pcmu_t *pcmu_p,
140     pci_regspec_t *rp)
141 {
142 	int assign_len, assign_entries, i;
143 	pci_regspec_t *assign_p;
144 	register uint32_t phys_hi = rp->pci_phys_hi;
145 	register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK;
146 	register uint32_t phys_addr = phys_hi & mask;
147 
148 	PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
149 	    "\tpcmu_reloc_reg fr: %x.%x.%x %x.%x\n",
150 	    rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
151 	    rp->pci_size_hi, rp->pci_size_low);
152 
153 	if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) {
154 		return (DDI_SUCCESS);
155 	}
156 
157 	/* phys_mid must be 0 regardless space type. XXX-64 bit mem space */
158 	if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) {
159 		PCMU_DBG0(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
160 		    "phys_mid or size_hi not 0\n");
161 		return (DDI_ME_INVAL);
162 	}
163 
164 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
165 	    "assigned-addresses", (caddr_t)&assign_p, &assign_len)) {
166 		return (DDI_ME_INVAL);
167 	}
168 
169 	assign_entries = assign_len / sizeof (pci_regspec_t);
170 	for (i = 0; i < assign_entries; i++, assign_p++) {
171 		if ((assign_p->pci_phys_hi & mask) == phys_addr) {
172 			rp->pci_phys_low += assign_p->pci_phys_low;
173 			break;
174 		}
175 	}
176 	kmem_free(assign_p - i, assign_len);
177 	PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
178 	    "\tpcmu_reloc_reg to: %x.%x.%x %x.%x\n",
179 	    rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
180 	    rp->pci_size_hi, rp->pci_size_low);
181 	return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
182 }
183 
184 /*
185  * use "ranges" to translate relocated pci regspec into parent space
186  */
187 int
pcmu_xlate_reg(pcmu_t * pcmu_p,pci_regspec_t * pcmu_rp,struct regspec * new_rp)188 pcmu_xlate_reg(pcmu_t *pcmu_p, pci_regspec_t *pcmu_rp, struct regspec *new_rp)
189 {
190 	int n;
191 	pcmu_ranges_t *rng_p = pcmu_p->pcmu_ranges;
192 	int rng_n = pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t);
193 
194 	uint32_t space_type = PCI_REG_ADDR_G(pcmu_rp->pci_phys_hi);
195 	uint32_t reg_end, reg_begin = pcmu_rp->pci_phys_low;
196 	uint32_t sz = pcmu_rp->pci_size_low;
197 
198 	uint32_t rng_begin, rng_end;
199 
200 	if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
201 		if (reg_begin > PCI_CONF_HDR_SIZE) {
202 			return (DDI_ME_INVAL);
203 		}
204 		sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
205 		reg_begin += pcmu_rp->pci_phys_hi;
206 	}
207 	reg_end = reg_begin + sz - 1;
208 
209 	for (n = 0; n < rng_n; n++, rng_p++) {
210 		if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) {
211 			continue;	/* not the same space type */
212 		}
213 
214 		rng_begin = rng_p->child_low;
215 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
216 			rng_begin += rng_p->child_high;
217 		}
218 		rng_end = rng_begin + rng_p->size_low - 1;
219 		if (reg_begin >= rng_begin && reg_end <= rng_end) {
220 			break;
221 		}
222 	}
223 	if (n >= rng_n) {
224 		return (DDI_ME_REGSPEC_RANGE);
225 	}
226 
227 	new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
228 	new_rp->regspec_bustype = rng_p->parent_high;
229 	new_rp->regspec_size = sz;
230 	PCMU_DBG4(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
231 	    "\tpcmu_xlate_reg: entry %d new_rp %x.%x %x\n",
232 	    n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
233 	return (DDI_SUCCESS);
234 }
235 
236 
237 /*
238  * pcmu_report_dev
239  *
240  * This function is called from our control ops routine on a
241  * DDI_CTLOPS_REPORTDEV request.
242  *
243  * The display format is
244  *
245  *	<name><inst> at <pname><pinst> device <dev> function <func>
246  *
247  * where
248  *
249  *	<name>		this device's name property
250  *	<inst>		this device's instance number
251  *	<name>		parent device's name property
252  *	<inst>		parent device's instance number
253  *	<dev>		this device's device number
254  *	<func>		this device's function number
255  */
256 int
pcmu_report_dev(dev_info_t * dip)257 pcmu_report_dev(dev_info_t *dip)
258 {
259 	if (dip == (dev_info_t *)0) {
260 		return (DDI_FAILURE);
261 	}
262 	cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(dip),
263 	    ddi_get_name_addr(dip), ddi_driver_name(dip),
264 	    ddi_get_instance(dip));
265 	return (DDI_SUCCESS);
266 }
267 
268 /*
269  * name_child
270  *
271  * This function is called from pcmu_init_child to name a node. It is
272  * also passed as a callback for node merging functions.
273  *
274  * return value: DDI_SUCCESS, DDI_FAILURE
275  */
276 static int
name_child(dev_info_t * child,char * name,int namelen)277 name_child(dev_info_t *child, char *name, int namelen)
278 {
279 	pci_regspec_t *pcmu_rp;
280 	int reglen;
281 	uint_t func;
282 	char **unit_addr;
283 	uint_t n;
284 
285 	/*
286 	 * Set the address portion of the node name based on
287 	 * unit-address property, if it exists.
288 	 * The interpretation of the unit-address is DD[,F]
289 	 * where DD is the device id and F is the function.
290 	 */
291 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
292 	    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
293 	    DDI_PROP_SUCCESS) {
294 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
295 			cmn_err(CE_WARN, "unit-address property in %s.conf"
296 			    " not well-formed", ddi_driver_name(child));
297 			ddi_prop_free(unit_addr);
298 			return (DDI_FAILURE);
299 		}
300 		(void) snprintf(name, namelen, "%s", *unit_addr);
301 		ddi_prop_free(unit_addr);
302 		return (DDI_SUCCESS);
303 	}
304 
305 	/*
306 	 * The unit-address property is does not exist. Set the address
307 	 * portion of the node name based on the function and device number.
308 	 */
309 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
310 	    "reg", (int **)&pcmu_rp, (uint_t *)&reglen) == DDI_SUCCESS) {
311 		if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
312 			cmn_err(CE_WARN, "reg property not well-formed");
313 			return (DDI_FAILURE);
314 		}
315 
316 		func = PCI_REG_FUNC_G(pcmu_rp[0].pci_phys_hi);
317 		if (func != 0) {
318 			(void) snprintf(name, namelen, "%x,%x",
319 			    PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi), func);
320 		} else {
321 			(void) snprintf(name, namelen, "%x",
322 			    PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi));
323 		}
324 		ddi_prop_free(pcmu_rp);
325 		return (DDI_SUCCESS);
326 	}
327 	cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
328 	return (DDI_FAILURE);
329 }
330 
331 int
pcmu_uninit_child(pcmu_t * pcmu_p,dev_info_t * child)332 pcmu_uninit_child(pcmu_t *pcmu_p, dev_info_t *child)
333 {
334 	PCMU_DBG2(PCMU_DBG_CTLOPS, pcmu_p->pcmu_dip,
335 	    "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
336 	    ddi_driver_name(child), ddi_get_instance(child));
337 
338 	ddi_set_name_addr(child, NULL);
339 	ddi_remove_minor_node(child, NULL);
340 	impl_rem_dev_props(child);
341 
342 	PCMU_DBG0(PCMU_DBG_PWR, ddi_get_parent(child), "\n\n");
343 	return (DDI_SUCCESS);
344 }
345 
346 /*
347  * pcmu_init_child
348  *
349  * This function is called from our control ops routine on a
350  * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
351  * parent private data area.
352  *
353  * used by: pcmu_ctlops()
354  *
355  * return value: none
356  */
357 int
pcmu_init_child(pcmu_t * pcmu_p,dev_info_t * child)358 pcmu_init_child(pcmu_t *pcmu_p, dev_info_t *child)
359 {
360 	char name[10];
361 	ddi_acc_handle_t config_handle;
362 	uint8_t bcr;
363 	uint8_t header_type;
364 
365 	if (name_child(child, name, 10) != DDI_SUCCESS)
366 		return (DDI_FAILURE);
367 	ddi_set_name_addr(child, name);
368 
369 	PCMU_DBG2(PCMU_DBG_PWR, ddi_get_parent(child),
370 	    "INITCHILD: config regs setup for %s@%s\n",
371 	    ddi_node_name(child), ddi_get_name_addr(child));
372 
373 	/*
374 	 * Map the child configuration space to for initialization.
375 	 * We assume the obp will do the following in the devices
376 	 * config space:
377 	 *
378 	 *	Set the latency-timer register to values appropriate
379 	 *	for the devices on the bus (based on other devices
380 	 *	MIN_GNT and MAX_LAT registers.
381 	 *
382 	 *	Set the fast back-to-back enable bit in the command
383 	 *	register if it's supported and all devices on the bus
384 	 *	have the capability.
385 	 *
386 	 */
387 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
388 		ddi_set_name_addr(child, NULL);
389 		return (DDI_FAILURE);
390 	}
391 
392 	/*
393 	 * Determine the configuration header type.
394 	 */
395 	header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
396 	PCMU_DBG2(PCMU_DBG_INIT_CLD, pcmu_p->pcmu_dip, "%s: header_type=%x\n",
397 	    ddi_driver_name(child), header_type);
398 
399 	/*
400 	 * If the device has a bus control register then program it
401 	 * based on the settings in the command register.
402 	 */
403 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
404 		bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
405 		if (pcmu_command_default & PCI_COMM_PARITY_DETECT)
406 			bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
407 		if (pcmu_command_default & PCI_COMM_SERR_ENABLE)
408 			bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
409 		bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
410 		pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
411 	}
412 
413 	pci_config_teardown(&config_handle);
414 	return (DDI_SUCCESS);
415 }
416 
417 /*
418  * pcmu_get_reg_set_size
419  *
420  * Given a dev info pointer to a pci child and a register number, this
421  * routine returns the size element of that reg set property.
422  *
423  * used by: pcmu_ctlops() - DDI_CTLOPS_REGSIZE
424  *
425  * return value: size of reg set on success, zero on error
426  */
427 off_t
pcmu_get_reg_set_size(dev_info_t * child,int rnumber)428 pcmu_get_reg_set_size(dev_info_t *child, int rnumber)
429 {
430 	pci_regspec_t *pcmu_rp;
431 	off_t size;
432 	int i;
433 
434 	if (rnumber < 0) {
435 		return (0);
436 	}
437 
438 	/*
439 	 * Get the reg property for the device.
440 	 */
441 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
442 	    (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
443 		return (0);
444 	}
445 
446 	if (rnumber >= (i / (int)sizeof (pci_regspec_t))) {
447 		kmem_free(pcmu_rp, i);
448 		return (0);
449 	}
450 
451 	size = pcmu_rp[rnumber].pci_size_low |
452 	    ((uint64_t)pcmu_rp[rnumber].pci_size_hi << 32);
453 	kmem_free(pcmu_rp, i);
454 	return (size);
455 }
456 
457 
458 /*
459  * pcmu_get_nreg_set
460  *
461  * Given a dev info pointer to a pci child, this routine returns the
462  * number of sets in its "reg" property.
463  *
464  * used by: pcmu_ctlops() - DDI_CTLOPS_NREGS
465  *
466  * return value: # of reg sets on success, zero on error
467  */
468 uint_t
pcmu_get_nreg_set(dev_info_t * child)469 pcmu_get_nreg_set(dev_info_t *child)
470 {
471 	pci_regspec_t *pcmu_rp;
472 	int i, n;
473 
474 	/*
475 	 * Get the reg property for the device.
476 	 */
477 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
478 	    (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
479 		return (0);
480 	}
481 	n = i / (int)sizeof (pci_regspec_t);
482 	kmem_free(pcmu_rp, i);
483 	return (n);
484 }
485 
486 int
pcmu_cfg_report(dev_info_t * dip,ddi_fm_error_t * derr,pcmu_errstate_t * pcmu_err_p,int caller,uint32_t prierr)487 pcmu_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr,
488     pcmu_errstate_t *pcmu_err_p, int caller, uint32_t prierr)
489 {
490 	int fatal = 0;
491 	int nonfatal = 0;
492 	int i;
493 	pcmu_t *pcmu_p;
494 	int instance = ddi_get_instance(dip);
495 
496 	ASSERT(dip);
497 
498 	pcmu_p = get_pcmu_soft_state(instance);
499 
500 	derr->fme_ena = derr->fme_ena ? derr->fme_ena :
501 	    fm_ena_generate(0, FM_ENA_FMT1);
502 
503 	for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
504 		if (pcmu_err_p->pcmu_cfg_stat & pci_err_tbl[i].reg_bit) {
505 			char buf[FM_MAX_CLASS];
506 			char *aux_msg = NULL;
507 
508 			switch (pci_err_tbl[i].reg_bit) {
509 			case PCI_STAT_R_MAST_AB:
510 				aux_msg = "Receieved Master Abort";
511 				/* FALLTHROUGH */
512 			case PCI_STAT_R_TARG_AB:
513 				if (aux_msg != NULL)
514 					aux_msg = "Receieved Target Abort";
515 				if (prierr) {
516 					/*
517 					 * piow case are already handled in
518 					 * pcmu_pbm_afsr_report()
519 					 */
520 					break;
521 				}
522 				if (caller != PCI_TRAP_CALL) {
523 					/*
524 					 * if we haven't come from trap handler
525 					 * we won't have an address
526 					 */
527 					fatal++;
528 				}
529 				break;
530 			default:
531 				/*
532 				 * dpe on dma write or ta on dma
533 				 */
534 				nonfatal++;
535 				break;
536 			}
537 			(void) snprintf(buf, FM_MAX_CLASS, "%s %s: %s %s",
538 			    (pcmu_p->pcmu_pcbm_p)->pcbm_nameinst_str,
539 			    (pcmu_p->pcmu_pcbm_p)->pcbm_nameaddr_str,
540 			    "PCI config space:", aux_msg);
541 			cmn_err(CE_WARN, "%s %s=0x%p", buf, "pbm-csr",
542 			    (void *)(pcmu_p->pcmu_pcbm_p)->pcbm_ctrl_reg);
543 		}
544 	}
545 
546 	if (fatal)
547 		return (DDI_FM_FATAL);
548 	else if (nonfatal)
549 		return (DDI_FM_NONFATAL);
550 
551 	return (DDI_FM_OK);
552 }
553 
554 void
pcmu_child_cfg_save(dev_info_t * dip)555 pcmu_child_cfg_save(dev_info_t *dip)
556 {
557 	dev_info_t *cdip;
558 	int ret = DDI_SUCCESS;
559 
560 	/*
561 	 * Save the state of the configuration headers of child
562 	 * nodes.
563 	 */
564 
565 	for (cdip = ddi_get_child(dip); cdip != NULL;
566 	    cdip = ddi_get_next_sibling(cdip)) {
567 
568 		/*
569 		 * Not interested in children who are not already
570 		 * init'ed.  They will be set up in pcmu_init_child().
571 		 */
572 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
573 			PCMU_DBG2(PCMU_DBG_DETACH, dip, "DDI_SUSPEND: skipping "
574 			    "%s%d not in CF1\n", ddi_driver_name(cdip),
575 			    ddi_get_instance(cdip));
576 
577 			continue;
578 		}
579 
580 		/*
581 		 * Only save config registers if not already saved by child.
582 		 */
583 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
584 		    SAVED_CONFIG_REGS) == 1) {
585 
586 			continue;
587 		}
588 
589 		/*
590 		 * The nexus needs to save config registers.  Create a property
591 		 * so it knows to restore on resume.
592 		 */
593 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
594 		    "nexus-saved-config-regs");
595 
596 		if (ret != DDI_PROP_SUCCESS) {
597 			cmn_err(CE_WARN, "%s%d can't update prop %s",
598 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
599 			    "nexus-saved-config-regs");
600 		}
601 
602 		(void) pci_save_config_regs(cdip);
603 	}
604 }
605 
606 void
pcmu_child_cfg_restore(dev_info_t * dip)607 pcmu_child_cfg_restore(dev_info_t *dip)
608 {
609 	dev_info_t *cdip;
610 
611 	/*
612 	 * Restore config registers for children that did not save
613 	 * their own registers.  Children pwr states are UNKNOWN after
614 	 * a resume since it is possible for the PM framework to call
615 	 * resume without an actual power cycle. (ie if suspend fails).
616 	 */
617 	for (cdip = ddi_get_child(dip); cdip != NULL;
618 	    cdip = ddi_get_next_sibling(cdip)) {
619 
620 		/*
621 		 * Not interested in children who are not already
622 		 * init'ed.  They will be set up by pcmu_init_child().
623 		 */
624 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
625 			PCMU_DBG2(PCMU_DBG_DETACH, dip,
626 			    "DDI_RESUME: skipping %s%d not in CF1\n",
627 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
628 			continue;
629 		}
630 
631 		/*
632 		 * Only restore config registers if saved by nexus.
633 		 */
634 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
635 		    "nexus-saved-config-regs") == 1) {
636 			(void) pci_restore_config_regs(cdip);
637 
638 			PCMU_DBG2(PCMU_DBG_PWR, dip,
639 			    "DDI_RESUME: nexus restoring %s%d config regs\n",
640 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
641 
642 			if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
643 			    "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
644 				cmn_err(CE_WARN, "%s%d can't remove prop %s",
645 				    ddi_driver_name(cdip),
646 				    ddi_get_instance(cdip),
647 				    "nexus-saved-config-regs");
648 			}
649 		}
650 	}
651 }
652 
653 #ifdef DEBUG
654 extern uint64_t pcmu_debug_flags;
655 
656 pcmu_dflag_to_str_t pcmu_dflag_strings [] = {
657 	{PCMU_DBG_ATTACH,	"pcmu_attach"},
658 	{PCMU_DBG_DETACH,	"pcmu_detach"},
659 	{PCMU_DBG_MAP,		"pcmu_map"},
660 	{PCMU_DBG_A_INTX,	"pcmu_add_intx"},
661 	{PCMU_DBG_R_INTX,	"pcmu_rem_intx"},
662 	{PCMU_DBG_INIT_CLD,	"pcmu_init_child"},
663 	{PCMU_DBG_CTLOPS,	"pcmu_ctlops"},
664 	{PCMU_DBG_INTR,		"pcmu_intr_wrapper"},
665 	{PCMU_DBG_ERR_INTR,	"pcmu_pbm_error_intr"},
666 	{PCMU_DBG_BUS_FAULT,	"pcmu_fault"},
667 	{PCMU_DBG_IB,		"pcmu_ib"},
668 	{PCMU_DBG_CB,		"pcmu_cb"},
669 	{PCMU_DBG_PBM,		"pcmu_pbm"},
670 	{PCMU_DBG_OPEN,		"pcmu_open"},
671 	{PCMU_DBG_CLOSE,	"pcmu_close"},
672 	{PCMU_DBG_IOCTL,	"pcmu_ioctl"},
673 	{PCMU_DBG_PWR,		"pcmu_pwr"}
674 };
675 
676 void
pcmu_debug(uint64_t flag,dev_info_t * dip,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)677 pcmu_debug(uint64_t flag, dev_info_t *dip, char *fmt,
678     uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
679 {
680 	char *s = "pcmu unknown";
681 	uint_t cont = 0;
682 	int i;
683 	int no_rec = (sizeof (pcmu_dflag_strings) /
684 	    sizeof (pcmu_dflag_to_str_t));
685 
686 	if (flag & PCMU_DBG_CONT) {
687 		flag &= ~PCMU_DBG_CONT;
688 		cont = 1;
689 	}
690 	if ((pcmu_debug_flags & flag) == flag) {
691 		for (i = 0; i < no_rec; i++) {
692 			if (pcmu_dflag_strings[i].flag == flag) {
693 				s = pcmu_dflag_strings[i].string;
694 				break;
695 			}
696 		}
697 		if (s && cont == 0) {
698 			prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
699 			    ddi_get_instance(dip), s);
700 		}
701 		prom_printf(fmt, a1, a2, a3, a4, a5);
702 	}
703 }
704 #endif
705