xref: /onnv-gate/usr/src/uts/sun4u/io/pci/pci_util.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * PCI nexus utility routines:
31*0Sstevel@tonic-gate  *	property and config routines for attach()
32*0Sstevel@tonic-gate  *	reg/intr/range/assigned-address property routines for bus_map()
33*0Sstevel@tonic-gate  *	init_child()
34*0Sstevel@tonic-gate  *	fault handling
35*0Sstevel@tonic-gate  */
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #include <sys/types.h>
38*0Sstevel@tonic-gate #include <sys/kmem.h>
39*0Sstevel@tonic-gate #include <sys/async.h>
40*0Sstevel@tonic-gate #include <sys/sysmacros.h>
41*0Sstevel@tonic-gate #include <sys/sunddi.h>
42*0Sstevel@tonic-gate #include <sys/sunndi.h>
43*0Sstevel@tonic-gate #include <sys/fm/protocol.h>
44*0Sstevel@tonic-gate #include <sys/fm/io/pci.h>
45*0Sstevel@tonic-gate #include <sys/fm/util.h>
46*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
47*0Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate /*LINTLIBRARY*/
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate /*
52*0Sstevel@tonic-gate  * get_pci_properties
53*0Sstevel@tonic-gate  *
54*0Sstevel@tonic-gate  * This function is called from the attach routine to get the key
55*0Sstevel@tonic-gate  * properties of the pci nodes.
56*0Sstevel@tonic-gate  *
57*0Sstevel@tonic-gate  * used by: pci_attach()
58*0Sstevel@tonic-gate  *
59*0Sstevel@tonic-gate  * return value: DDI_FAILURE on failure
60*0Sstevel@tonic-gate  */
61*0Sstevel@tonic-gate int
62*0Sstevel@tonic-gate get_pci_properties(pci_t *pci_p, dev_info_t *dip)
63*0Sstevel@tonic-gate {
64*0Sstevel@tonic-gate 	int i;
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate 	/*
67*0Sstevel@tonic-gate 	 * Get the device's port id.
68*0Sstevel@tonic-gate 	 */
69*0Sstevel@tonic-gate 	if ((pci_p->pci_id = (uint32_t)pci_get_portid(dip)) == -1u) {
70*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d: no portid property\n",
71*0Sstevel@tonic-gate 			ddi_driver_name(dip), ddi_get_instance(dip));
72*0Sstevel@tonic-gate 		return (DDI_FAILURE);
73*0Sstevel@tonic-gate 	}
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate 	/*
76*0Sstevel@tonic-gate 	 * Get the bus-ranges property.
77*0Sstevel@tonic-gate 	 */
78*0Sstevel@tonic-gate 	i = sizeof (pci_p->pci_bus_range);
79*0Sstevel@tonic-gate 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
80*0Sstevel@tonic-gate 	    "bus-range", (caddr_t)&pci_p->pci_bus_range, &i) != DDI_SUCCESS) {
81*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d: no bus-range property\n",
82*0Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip));
83*0Sstevel@tonic-gate 		return (DDI_FAILURE);
84*0Sstevel@tonic-gate 	}
85*0Sstevel@tonic-gate 	DEBUG2(DBG_ATTACH, dip, "get_pci_properties: bus-range (%x,%x)\n",
86*0Sstevel@tonic-gate 		pci_p->pci_bus_range.lo, pci_p->pci_bus_range.hi);
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	/*
89*0Sstevel@tonic-gate 	 * disable streaming cache if necessary, this must be done
90*0Sstevel@tonic-gate 	 * before PBM is configured.
91*0Sstevel@tonic-gate 	 */
92*0Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
93*0Sstevel@tonic-gate 			"no-streaming-cache")) {
94*0Sstevel@tonic-gate 		pci_stream_buf_enable = 0;
95*0Sstevel@tonic-gate 		pci_stream_buf_exists = 0;
96*0Sstevel@tonic-gate 	}
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 	/*
99*0Sstevel@tonic-gate 	 * Get the ranges property.
100*0Sstevel@tonic-gate 	 */
101*0Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
102*0Sstevel@tonic-gate 		(caddr_t)&pci_p->pci_ranges, &pci_p->pci_ranges_length) !=
103*0Sstevel@tonic-gate 		DDI_SUCCESS) {
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d: no ranges property\n",
106*0Sstevel@tonic-gate 			ddi_driver_name(dip), ddi_get_instance(dip));
107*0Sstevel@tonic-gate 		return (DDI_FAILURE);
108*0Sstevel@tonic-gate 	}
109*0Sstevel@tonic-gate 	pci_fix_ranges(pci_p->pci_ranges,
110*0Sstevel@tonic-gate 		pci_p->pci_ranges_length / sizeof (pci_ranges_t));
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 	/*
113*0Sstevel@tonic-gate 	 * Determine the number upa slot interrupts.
114*0Sstevel@tonic-gate 	 */
115*0Sstevel@tonic-gate 	pci_p->pci_numproxy = pci_get_numproxy(pci_p->pci_dip);
116*0Sstevel@tonic-gate 	DEBUG1(DBG_ATTACH, dip, "get_pci_properties: numproxy=%d\n",
117*0Sstevel@tonic-gate 	    pci_p->pci_numproxy);
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate 	pci_p->pci_thermal_interrupt =
120*0Sstevel@tonic-gate 		ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
121*0Sstevel@tonic-gate 				"thermal-interrupt", -1);
122*0Sstevel@tonic-gate 	DEBUG1(DBG_ATTACH, dip, "get_pci_properties: thermal_interrupt=%d\n",
123*0Sstevel@tonic-gate 	    pci_p->pci_thermal_interrupt);
124*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
125*0Sstevel@tonic-gate }
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /*
128*0Sstevel@tonic-gate  * free_pci_properties:
129*0Sstevel@tonic-gate  *
130*0Sstevel@tonic-gate  * This routine frees the memory used to cache the
131*0Sstevel@tonic-gate  * "ranges" properties of the pci bus device node.
132*0Sstevel@tonic-gate  *
133*0Sstevel@tonic-gate  * used by: pci_detach()
134*0Sstevel@tonic-gate  *
135*0Sstevel@tonic-gate  * return value: none
136*0Sstevel@tonic-gate  */
137*0Sstevel@tonic-gate void
138*0Sstevel@tonic-gate free_pci_properties(pci_t *pci_p)
139*0Sstevel@tonic-gate {
140*0Sstevel@tonic-gate 	kmem_free(pci_p->pci_ranges, pci_p->pci_ranges_length);
141*0Sstevel@tonic-gate }
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate /*
144*0Sstevel@tonic-gate  * pci_reloc_reg
145*0Sstevel@tonic-gate  *
146*0Sstevel@tonic-gate  * If the "reg" entry (*pci_rp) is relocatable, lookup "assigned-addresses"
147*0Sstevel@tonic-gate  * property to fetch corresponding relocated address.
148*0Sstevel@tonic-gate  *
149*0Sstevel@tonic-gate  * used by: pci_map()
150*0Sstevel@tonic-gate  *
151*0Sstevel@tonic-gate  * return value:
152*0Sstevel@tonic-gate  *
153*0Sstevel@tonic-gate  *	DDI_SUCCESS		- on success
154*0Sstevel@tonic-gate  *	DDI_ME_INVAL		- regspec is invalid
155*0Sstevel@tonic-gate  */
156*0Sstevel@tonic-gate int
157*0Sstevel@tonic-gate pci_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pci_t *pci_p,
158*0Sstevel@tonic-gate 	pci_regspec_t *rp)
159*0Sstevel@tonic-gate {
160*0Sstevel@tonic-gate 	int assign_len, assign_entries, i;
161*0Sstevel@tonic-gate 	pci_regspec_t *assign_p;
162*0Sstevel@tonic-gate 	register uint32_t phys_hi = rp->pci_phys_hi;
163*0Sstevel@tonic-gate 	register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK;
164*0Sstevel@tonic-gate 	register uint32_t phys_addr = phys_hi & mask;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	DEBUG5(DBG_MAP | DBG_CONT, dip, "\tpci_reloc_reg fr: %x.%x.%x %x.%x\n",
167*0Sstevel@tonic-gate 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
168*0Sstevel@tonic-gate 		rp->pci_size_hi, rp->pci_size_low);
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK))
171*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	if (pci_p->hotplug_capable == B_FALSE) {	/* validate bus # */
174*0Sstevel@tonic-gate 		uint32_t bus = PCI_REG_BUS_G(phys_hi);
175*0Sstevel@tonic-gate 		if (bus < pci_p->pci_bus_range.lo ||
176*0Sstevel@tonic-gate 		    bus > pci_p->pci_bus_range.hi) {
177*0Sstevel@tonic-gate 			DEBUG1(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus);
178*0Sstevel@tonic-gate 			return (DDI_ME_INVAL);
179*0Sstevel@tonic-gate 		}
180*0Sstevel@tonic-gate 	}
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	/* phys_mid must be 0 regardless space type. */
183*0Sstevel@tonic-gate 	if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) {
184*0Sstevel@tonic-gate 		DEBUG0(DBG_MAP | DBG_CONT, pci_p->pci_dip,
185*0Sstevel@tonic-gate 			"phys_mid or size_hi not 0\n");
186*0Sstevel@tonic-gate 		return (DDI_ME_INVAL);
187*0Sstevel@tonic-gate 	}
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
190*0Sstevel@tonic-gate 		"assigned-addresses", (caddr_t)&assign_p, &assign_len))
191*0Sstevel@tonic-gate 		return (DDI_ME_INVAL);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	assign_entries = assign_len / sizeof (pci_regspec_t);
194*0Sstevel@tonic-gate 	for (i = 0; i < assign_entries; i++, assign_p++) {
195*0Sstevel@tonic-gate 		if ((assign_p->pci_phys_hi & mask) == phys_addr) {
196*0Sstevel@tonic-gate 			rp->pci_phys_low += assign_p->pci_phys_low;
197*0Sstevel@tonic-gate 			break;
198*0Sstevel@tonic-gate 		}
199*0Sstevel@tonic-gate 	}
200*0Sstevel@tonic-gate 	kmem_free(assign_p - i, assign_len);
201*0Sstevel@tonic-gate 	DEBUG5(DBG_MAP | DBG_CONT, dip, "\tpci_reloc_reg to: %x.%x.%x %x.%x\n",
202*0Sstevel@tonic-gate 		rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
203*0Sstevel@tonic-gate 		rp->pci_size_hi, rp->pci_size_low);
204*0Sstevel@tonic-gate 	return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
205*0Sstevel@tonic-gate }
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate /*
208*0Sstevel@tonic-gate  * use "ranges" to translate relocated pci regspec into parent space
209*0Sstevel@tonic-gate  */
210*0Sstevel@tonic-gate int
211*0Sstevel@tonic-gate pci_xlate_reg(pci_t *pci_p, pci_regspec_t *pci_rp, struct regspec *new_rp)
212*0Sstevel@tonic-gate {
213*0Sstevel@tonic-gate 	int n;
214*0Sstevel@tonic-gate 	pci_ranges_t *rng_p = pci_p->pci_ranges;
215*0Sstevel@tonic-gate 	int rng_n = pci_p->pci_ranges_length / sizeof (pci_ranges_t);
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	uint32_t space_type = PCI_REG_ADDR_G(pci_rp->pci_phys_hi);
218*0Sstevel@tonic-gate 	uint32_t reg_end, reg_begin = pci_rp->pci_phys_low;
219*0Sstevel@tonic-gate 	uint32_t sz = pci_rp->pci_size_low;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	uint32_t rng_begin, rng_end;
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
224*0Sstevel@tonic-gate 		if (reg_begin > PCI_CONF_HDR_SIZE)
225*0Sstevel@tonic-gate 			return (DDI_ME_INVAL);
226*0Sstevel@tonic-gate 		sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
227*0Sstevel@tonic-gate 		reg_begin += pci_rp->pci_phys_hi;
228*0Sstevel@tonic-gate 	}
229*0Sstevel@tonic-gate 	reg_end = reg_begin + sz - 1;
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	for (n = 0; n < rng_n; n++, rng_p++) {
232*0Sstevel@tonic-gate 		if (space_type != PCI_REG_ADDR_G(rng_p->child_high))
233*0Sstevel@tonic-gate 			continue;	/* not the same space type */
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate 		rng_begin = rng_p->child_low;
236*0Sstevel@tonic-gate 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
237*0Sstevel@tonic-gate 			rng_begin += rng_p->child_high;
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 		rng_end = rng_begin + rng_p->size_low - 1;
240*0Sstevel@tonic-gate 		if (reg_begin >= rng_begin && reg_end <= rng_end)
241*0Sstevel@tonic-gate 			break;
242*0Sstevel@tonic-gate 	}
243*0Sstevel@tonic-gate 	if (n >= rng_n)
244*0Sstevel@tonic-gate 		return (DDI_ME_REGSPEC_RANGE);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
247*0Sstevel@tonic-gate 	new_rp->regspec_bustype = rng_p->parent_high;
248*0Sstevel@tonic-gate 	new_rp->regspec_size = sz;
249*0Sstevel@tonic-gate 	DEBUG4(DBG_MAP | DBG_CONT, pci_p->pci_dip,
250*0Sstevel@tonic-gate 		"\tpci_xlate_reg: entry %d new_rp %x.%x %x\n",
251*0Sstevel@tonic-gate 		n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
254*0Sstevel@tonic-gate }
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate /*
258*0Sstevel@tonic-gate  * report_dev
259*0Sstevel@tonic-gate  *
260*0Sstevel@tonic-gate  * This function is called from our control ops routine on a
261*0Sstevel@tonic-gate  * DDI_CTLOPS_REPORTDEV request.
262*0Sstevel@tonic-gate  *
263*0Sstevel@tonic-gate  * The display format is
264*0Sstevel@tonic-gate  *
265*0Sstevel@tonic-gate  *	<name><inst> at <pname><pinst> device <dev> function <func>
266*0Sstevel@tonic-gate  *
267*0Sstevel@tonic-gate  * where
268*0Sstevel@tonic-gate  *
269*0Sstevel@tonic-gate  *	<name>		this device's name property
270*0Sstevel@tonic-gate  *	<inst>		this device's instance number
271*0Sstevel@tonic-gate  *	<name>		parent device's name property
272*0Sstevel@tonic-gate  *	<inst>		parent device's instance number
273*0Sstevel@tonic-gate  *	<dev>		this device's device number
274*0Sstevel@tonic-gate  *	<func>		this device's function number
275*0Sstevel@tonic-gate  */
276*0Sstevel@tonic-gate int
277*0Sstevel@tonic-gate report_dev(dev_info_t *dip)
278*0Sstevel@tonic-gate {
279*0Sstevel@tonic-gate 	if (dip == (dev_info_t *)0)
280*0Sstevel@tonic-gate 		return (DDI_FAILURE);
281*0Sstevel@tonic-gate 	cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
282*0Sstevel@tonic-gate 	    ddi_node_name(dip), ddi_get_name_addr(dip),
283*0Sstevel@tonic-gate 	    ddi_driver_name(dip),
284*0Sstevel@tonic-gate 	    ddi_get_instance(dip));
285*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
286*0Sstevel@tonic-gate }
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate /*
290*0Sstevel@tonic-gate  * reg property for pcimem nodes that covers the entire address
291*0Sstevel@tonic-gate  * space for the node:  config, io, or memory.
292*0Sstevel@tonic-gate  */
293*0Sstevel@tonic-gate pci_regspec_t pci_pcimem_reg[3] =
294*0Sstevel@tonic-gate {
295*0Sstevel@tonic-gate 	{PCI_ADDR_CONFIG,			0, 0, 0, 0x800000	},
296*0Sstevel@tonic-gate 	{(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B),	0, 0, 0, PCI_IO_SIZE	},
297*0Sstevel@tonic-gate 	{(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PCI_MEM_SIZE	}
298*0Sstevel@tonic-gate };
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate /*
301*0Sstevel@tonic-gate  * name_child
302*0Sstevel@tonic-gate  *
303*0Sstevel@tonic-gate  * This function is called from init_child to name a node. It is
304*0Sstevel@tonic-gate  * also passed as a callback for node merging functions.
305*0Sstevel@tonic-gate  *
306*0Sstevel@tonic-gate  * return value: DDI_SUCCESS, DDI_FAILURE
307*0Sstevel@tonic-gate  */
308*0Sstevel@tonic-gate static int
309*0Sstevel@tonic-gate name_child(dev_info_t *child, char *name, int namelen)
310*0Sstevel@tonic-gate {
311*0Sstevel@tonic-gate 	pci_regspec_t *pci_rp;
312*0Sstevel@tonic-gate 	int reglen;
313*0Sstevel@tonic-gate 	uint_t func;
314*0Sstevel@tonic-gate 	char **unit_addr;
315*0Sstevel@tonic-gate 	uint_t n;
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	/*
318*0Sstevel@tonic-gate 	 * Set the address portion of the node name based on
319*0Sstevel@tonic-gate 	 * unit-address property, if it exists.
320*0Sstevel@tonic-gate 	 * The interpretation of the unit-address is DD[,F]
321*0Sstevel@tonic-gate 	 * where DD is the device id and F is the function.
322*0Sstevel@tonic-gate 	 */
323*0Sstevel@tonic-gate 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
324*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
325*0Sstevel@tonic-gate 	    DDI_PROP_SUCCESS) {
326*0Sstevel@tonic-gate 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
327*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "unit-address property in %s.conf"
328*0Sstevel@tonic-gate 			    " not well-formed", ddi_driver_name(child));
329*0Sstevel@tonic-gate 			ddi_prop_free(unit_addr);
330*0Sstevel@tonic-gate 			return (DDI_FAILURE);
331*0Sstevel@tonic-gate 		}
332*0Sstevel@tonic-gate 		(void) snprintf(name, namelen, "%s", *unit_addr);
333*0Sstevel@tonic-gate 		ddi_prop_free(unit_addr);
334*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
335*0Sstevel@tonic-gate 	}
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 	/*
338*0Sstevel@tonic-gate 	 * The unit-address property is does not exist. Set the address
339*0Sstevel@tonic-gate 	 * portion of the node name based on the function and device number.
340*0Sstevel@tonic-gate 	 */
341*0Sstevel@tonic-gate 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
342*0Sstevel@tonic-gate 	    "reg", (int **)&pci_rp, (uint_t *)&reglen) == DDI_SUCCESS) {
343*0Sstevel@tonic-gate 		if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
344*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "reg property not well-formed");
345*0Sstevel@tonic-gate 			return (DDI_FAILURE);
346*0Sstevel@tonic-gate 		}
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 		func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi);
349*0Sstevel@tonic-gate 		if (func != 0)
350*0Sstevel@tonic-gate 			(void) snprintf(name, namelen, "%x,%x",
351*0Sstevel@tonic-gate 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func);
352*0Sstevel@tonic-gate 		else
353*0Sstevel@tonic-gate 			(void) snprintf(name, namelen, "%x",
354*0Sstevel@tonic-gate 				PCI_REG_DEV_G(pci_rp[0].pci_phys_hi));
355*0Sstevel@tonic-gate 		ddi_prop_free(pci_rp);
356*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
360*0Sstevel@tonic-gate 	return (DDI_FAILURE);
361*0Sstevel@tonic-gate }
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate int
364*0Sstevel@tonic-gate uninit_child(pci_t *pci_p, dev_info_t *child)
365*0Sstevel@tonic-gate {
366*0Sstevel@tonic-gate 	DEBUG2(DBG_CTLOPS, pci_p->pci_dip,
367*0Sstevel@tonic-gate 	    "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
368*0Sstevel@tonic-gate 	    ddi_driver_name(child), ddi_get_instance(child));
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	(void) pm_uninit_child(child);
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 	ddi_set_name_addr(child, NULL);
374*0Sstevel@tonic-gate 	ddi_remove_minor_node(child, NULL);
375*0Sstevel@tonic-gate 	impl_rem_dev_props(child);
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	DEBUG0(DBG_PWR, ddi_get_parent(child), "\n\n");
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	/*
380*0Sstevel@tonic-gate 	 * Handle chip specific post-uninit-child tasks.
381*0Sstevel@tonic-gate 	 */
382*0Sstevel@tonic-gate 	pci_post_uninit_child(pci_p);
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
385*0Sstevel@tonic-gate }
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate /*
388*0Sstevel@tonic-gate  * init_child
389*0Sstevel@tonic-gate  *
390*0Sstevel@tonic-gate  * This function is called from our control ops routine on a
391*0Sstevel@tonic-gate  * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
392*0Sstevel@tonic-gate  * parent private data area.
393*0Sstevel@tonic-gate  *
394*0Sstevel@tonic-gate  * used by: pci_ctlops()
395*0Sstevel@tonic-gate  *
396*0Sstevel@tonic-gate  * return value: none
397*0Sstevel@tonic-gate  */
398*0Sstevel@tonic-gate int
399*0Sstevel@tonic-gate init_child(pci_t *pci_p, dev_info_t *child)
400*0Sstevel@tonic-gate {
401*0Sstevel@tonic-gate 	pci_regspec_t *pci_rp;
402*0Sstevel@tonic-gate 	char name[10];
403*0Sstevel@tonic-gate 	ddi_acc_handle_t config_handle;
404*0Sstevel@tonic-gate 	uint16_t command_preserve, command;
405*0Sstevel@tonic-gate 	uint8_t bcr;
406*0Sstevel@tonic-gate 	uint8_t header_type, min_gnt;
407*0Sstevel@tonic-gate 	uint16_t latency_timer;
408*0Sstevel@tonic-gate 	uint_t n;
409*0Sstevel@tonic-gate 	int i, no_config;
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	/*
412*0Sstevel@tonic-gate 	 * The following is a special case for pcimem nodes.
413*0Sstevel@tonic-gate 	 * For these nodes we create a reg property with a
414*0Sstevel@tonic-gate 	 * single entry that covers the entire address space
415*0Sstevel@tonic-gate 	 * for the node (config, io or memory).
416*0Sstevel@tonic-gate 	 */
417*0Sstevel@tonic-gate 	if (strcmp(ddi_driver_name(child), "pcimem") == 0) {
418*0Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, child,
419*0Sstevel@tonic-gate 		    DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg,
420*0Sstevel@tonic-gate 		    sizeof (pci_pcimem_reg));
421*0Sstevel@tonic-gate 		ddi_set_name_addr(child, "0");
422*0Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
423*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
424*0Sstevel@tonic-gate 	}
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * Check whether the node has config space or is a hard decode
428*0Sstevel@tonic-gate 	 * node (possibly created by a driver.conf file).
429*0Sstevel@tonic-gate 	 */
430*0Sstevel@tonic-gate 	no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
431*0Sstevel@tonic-gate 	    "no-config", 0);
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	/*
434*0Sstevel@tonic-gate 	 * Pseudo nodes indicate a prototype node with per-instance
435*0Sstevel@tonic-gate 	 * properties to be merged into the real h/w device node.
436*0Sstevel@tonic-gate 	 * However, do not merge if the no-config property is set
437*0Sstevel@tonic-gate 	 * (see PSARC 2000/088).
438*0Sstevel@tonic-gate 	 */
439*0Sstevel@tonic-gate 	if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) {
440*0Sstevel@tonic-gate 		extern int pci_allow_pseudo_children;
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY, child,
443*0Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) ==
444*0Sstevel@tonic-gate 		    DDI_SUCCESS) {
445*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "cannot merge prototype from %s.conf",
446*0Sstevel@tonic-gate 			    ddi_driver_name(child));
447*0Sstevel@tonic-gate 			kmem_free(pci_rp, i);
448*0Sstevel@tonic-gate 			return (DDI_NOT_WELL_FORMED);
449*0Sstevel@tonic-gate 		}
450*0Sstevel@tonic-gate 		/*
451*0Sstevel@tonic-gate 		 * Name the child
452*0Sstevel@tonic-gate 		 */
453*0Sstevel@tonic-gate 		if (name_child(child, name, 10) != DDI_SUCCESS)
454*0Sstevel@tonic-gate 			return (DDI_FAILURE);
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
457*0Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 		/*
460*0Sstevel@tonic-gate 		 * Try to merge the properties from this prototype
461*0Sstevel@tonic-gate 		 * node into real h/w nodes.
462*0Sstevel@tonic-gate 		 */
463*0Sstevel@tonic-gate 		if (ndi_merge_node(child, name_child) == DDI_SUCCESS) {
464*0Sstevel@tonic-gate 			/*
465*0Sstevel@tonic-gate 			 * Merged ok - return failure to remove the node.
466*0Sstevel@tonic-gate 			 */
467*0Sstevel@tonic-gate 			ddi_set_name_addr(child, NULL);
468*0Sstevel@tonic-gate 			return (DDI_FAILURE);
469*0Sstevel@tonic-gate 		}
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate 		/* workaround for ddivs to run under PCI */
472*0Sstevel@tonic-gate 		if (pci_allow_pseudo_children)
473*0Sstevel@tonic-gate 			return (DDI_SUCCESS);
474*0Sstevel@tonic-gate 
475*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
476*0Sstevel@tonic-gate 		    ddi_driver_name(child), ddi_get_name_addr(child),
477*0Sstevel@tonic-gate 		    ddi_driver_name(child));
478*0Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
479*0Sstevel@tonic-gate 		return (DDI_NOT_WELL_FORMED);
480*0Sstevel@tonic-gate 	}
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 	if (name_child(child, name, 10) != DDI_SUCCESS)
483*0Sstevel@tonic-gate 		return (DDI_FAILURE);
484*0Sstevel@tonic-gate 	ddi_set_name_addr(child, name);
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	if (no_config != 0) {
487*0Sstevel@tonic-gate 		/*
488*0Sstevel@tonic-gate 		 * There is no config space so there's nothing more to do.
489*0Sstevel@tonic-gate 		 */
490*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
491*0Sstevel@tonic-gate 	}
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	if (pm_init_child(child) != DDI_SUCCESS)
494*0Sstevel@tonic-gate 		return (DDI_FAILURE);
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 
497*0Sstevel@tonic-gate 	/*
498*0Sstevel@tonic-gate 	 * If configuration registers were previously saved by
499*0Sstevel@tonic-gate 	 * child (before it went to D3), then let the child do the
500*0Sstevel@tonic-gate 	 * restore to set up the config regs as it'll first need to
501*0Sstevel@tonic-gate 	 * power the device out of D3.
502*0Sstevel@tonic-gate 	 */
503*0Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
504*0Sstevel@tonic-gate 	    "config-regs-saved-by-child") == 1) {
505*0Sstevel@tonic-gate 		DEBUG0(DBG_PWR, child,
506*0Sstevel@tonic-gate 		    "INITCHILD: config regs to be restored by child\n");
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
509*0Sstevel@tonic-gate 	}
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	DEBUG2(DBG_PWR, ddi_get_parent(child),
512*0Sstevel@tonic-gate 	    "INITCHILD: config regs setup for %s@%s\n",
513*0Sstevel@tonic-gate 	    ddi_node_name(child), ddi_get_name_addr(child));
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/*
516*0Sstevel@tonic-gate 	 * Map the child configuration space to for initialization.
517*0Sstevel@tonic-gate 	 * We assume the obp will do the following in the devices
518*0Sstevel@tonic-gate 	 * config space:
519*0Sstevel@tonic-gate 	 *
520*0Sstevel@tonic-gate 	 *	Set the latency-timer register to values appropriate
521*0Sstevel@tonic-gate 	 *	for the devices on the bus (based on other devices
522*0Sstevel@tonic-gate 	 *	MIN_GNT and MAX_LAT registers.
523*0Sstevel@tonic-gate 	 *
524*0Sstevel@tonic-gate 	 *	Set the fast back-to-back enable bit in the command
525*0Sstevel@tonic-gate 	 *	register if it's supported and all devices on the bus
526*0Sstevel@tonic-gate 	 *	have the capability.
527*0Sstevel@tonic-gate 	 *
528*0Sstevel@tonic-gate 	 */
529*0Sstevel@tonic-gate 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
530*0Sstevel@tonic-gate 		(void) pm_uninit_child(child);
531*0Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 		return (DDI_FAILURE);
534*0Sstevel@tonic-gate 	}
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	/*
537*0Sstevel@tonic-gate 	 * Determine the configuration header type.
538*0Sstevel@tonic-gate 	 */
539*0Sstevel@tonic-gate 	header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
540*0Sstevel@tonic-gate 	DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: header_type=%x\n",
541*0Sstevel@tonic-gate 	    ddi_driver_name(child), header_type);
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	/*
544*0Sstevel@tonic-gate 	 * Support for "command-preserve" property.  Note that we
545*0Sstevel@tonic-gate 	 * add PCI_COMM_BACK2BACK_ENAB to the bits to be preserved
546*0Sstevel@tonic-gate 	 * since the obp will set this if the device supports and
547*0Sstevel@tonic-gate 	 * all targets on the same bus support it.  Since psycho
548*0Sstevel@tonic-gate 	 * doesn't support PCI_COMM_BACK2BACK_ENAB, it will never
549*0Sstevel@tonic-gate 	 * be set.  This is just here in case future revs do support
550*0Sstevel@tonic-gate 	 * PCI_COMM_BACK2BACK_ENAB.
551*0Sstevel@tonic-gate 	 */
552*0Sstevel@tonic-gate 	command_preserve =
553*0Sstevel@tonic-gate 	    ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
554*0Sstevel@tonic-gate 		"command-preserve", 0);
555*0Sstevel@tonic-gate 	DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: command-preserve=%x\n",
556*0Sstevel@tonic-gate 	    ddi_driver_name(child), command_preserve);
557*0Sstevel@tonic-gate 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
558*0Sstevel@tonic-gate 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
559*0Sstevel@tonic-gate 	command |= (pci_command_default & ~command_preserve);
560*0Sstevel@tonic-gate 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
561*0Sstevel@tonic-gate 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
562*0Sstevel@tonic-gate 	DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: command=%x\n",
563*0Sstevel@tonic-gate 	    ddi_driver_name(child),
564*0Sstevel@tonic-gate 	    pci_config_get16(config_handle, PCI_CONF_COMM));
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 	/*
567*0Sstevel@tonic-gate 	 * If the device has a bus control register then program it
568*0Sstevel@tonic-gate 	 * based on the settings in the command register.
569*0Sstevel@tonic-gate 	 */
570*0Sstevel@tonic-gate 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
571*0Sstevel@tonic-gate 		bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
572*0Sstevel@tonic-gate 		if (pci_command_default & PCI_COMM_PARITY_DETECT)
573*0Sstevel@tonic-gate 			bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
574*0Sstevel@tonic-gate 		if (pci_command_default & PCI_COMM_SERR_ENABLE)
575*0Sstevel@tonic-gate 			bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
576*0Sstevel@tonic-gate 		bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
577*0Sstevel@tonic-gate 		pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
578*0Sstevel@tonic-gate 	}
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate 	/*
581*0Sstevel@tonic-gate 	 * Initialize cache-line-size configuration register if needed.
582*0Sstevel@tonic-gate 	 */
583*0Sstevel@tonic-gate 	if (pci_set_cache_line_size_register &&
584*0Sstevel@tonic-gate 	    ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
585*0Sstevel@tonic-gate 		"cache-line-size", 0) == 0) {
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 		pci_config_put8(config_handle, PCI_CONF_CACHE_LINESZ,
588*0Sstevel@tonic-gate 		    PCI_CACHE_LINE_SIZE);
589*0Sstevel@tonic-gate 		n = pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ);
590*0Sstevel@tonic-gate 		if (n != 0)
591*0Sstevel@tonic-gate 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, child,
592*0Sstevel@tonic-gate 			    "cache-line-size", n);
593*0Sstevel@tonic-gate 	}
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 	/*
596*0Sstevel@tonic-gate 	 * Initialize latency timer registers if needed.
597*0Sstevel@tonic-gate 	 */
598*0Sstevel@tonic-gate 	if (pci_set_latency_timer_register &&
599*0Sstevel@tonic-gate 	    ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
600*0Sstevel@tonic-gate 		"latency-timer", 0) == 0) {
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 		latency_timer = pci_latency_timer;
603*0Sstevel@tonic-gate 		if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
604*0Sstevel@tonic-gate 			pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER,
605*0Sstevel@tonic-gate 			    latency_timer);
606*0Sstevel@tonic-gate 		} else {
607*0Sstevel@tonic-gate 			min_gnt = pci_config_get8(config_handle,
608*0Sstevel@tonic-gate 			    PCI_CONF_MIN_G);
609*0Sstevel@tonic-gate 			DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: min_gnt=%x\n",
610*0Sstevel@tonic-gate 			    ddi_driver_name(child), min_gnt);
611*0Sstevel@tonic-gate 			if (min_gnt != 0) {
612*0Sstevel@tonic-gate 				switch (pci_p->pci_pbm_p->pbm_speed) {
613*0Sstevel@tonic-gate 				case PBM_SPEED_33MHZ:
614*0Sstevel@tonic-gate 					latency_timer = min_gnt * 8;
615*0Sstevel@tonic-gate 					break;
616*0Sstevel@tonic-gate 				case PBM_SPEED_66MHZ:
617*0Sstevel@tonic-gate 					latency_timer = min_gnt * 4;
618*0Sstevel@tonic-gate 					break;
619*0Sstevel@tonic-gate 				}
620*0Sstevel@tonic-gate 			}
621*0Sstevel@tonic-gate 		}
622*0Sstevel@tonic-gate 		latency_timer = MIN(latency_timer, 0xff);
623*0Sstevel@tonic-gate 		pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER,
624*0Sstevel@tonic-gate 		    latency_timer);
625*0Sstevel@tonic-gate 		n = pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER);
626*0Sstevel@tonic-gate 		if (n != 0)
627*0Sstevel@tonic-gate 			(void) ndi_prop_update_int(DDI_DEV_T_NONE, child,
628*0Sstevel@tonic-gate 			    "latency-timer", n);
629*0Sstevel@tonic-gate 	}
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	pci_config_teardown(&config_handle);
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 	/*
634*0Sstevel@tonic-gate 	 * Handle chip specific init-child tasks.
635*0Sstevel@tonic-gate 	 */
636*0Sstevel@tonic-gate 	pci_post_init_child(pci_p, child);
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
639*0Sstevel@tonic-gate }
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate /*
642*0Sstevel@tonic-gate  * get_nreg_set
643*0Sstevel@tonic-gate  *
644*0Sstevel@tonic-gate  * Given a dev info pointer to a pci child, this routine returns the
645*0Sstevel@tonic-gate  * number of sets in its "reg" property.
646*0Sstevel@tonic-gate  *
647*0Sstevel@tonic-gate  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
648*0Sstevel@tonic-gate  *
649*0Sstevel@tonic-gate  * return value: # of reg sets on success, zero on error
650*0Sstevel@tonic-gate  */
651*0Sstevel@tonic-gate uint_t
652*0Sstevel@tonic-gate get_nreg_set(dev_info_t *child)
653*0Sstevel@tonic-gate {
654*0Sstevel@tonic-gate 	pci_regspec_t *pci_rp;
655*0Sstevel@tonic-gate 	int i, n;
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 	/*
658*0Sstevel@tonic-gate 	 * Get the reg property for the device.
659*0Sstevel@tonic-gate 	 */
660*0Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
661*0Sstevel@tonic-gate 	    (caddr_t)&pci_rp, &i) != DDI_SUCCESS)
662*0Sstevel@tonic-gate 		return (0);
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	n = i / (int)sizeof (pci_regspec_t);
665*0Sstevel@tonic-gate 	kmem_free(pci_rp, i);
666*0Sstevel@tonic-gate 	return (n);
667*0Sstevel@tonic-gate }
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate /*
671*0Sstevel@tonic-gate  * get_nintr
672*0Sstevel@tonic-gate  *
673*0Sstevel@tonic-gate  * Given a dev info pointer to a pci child, this routine returns the
674*0Sstevel@tonic-gate  * number of items in its "interrupts" property.
675*0Sstevel@tonic-gate  *
676*0Sstevel@tonic-gate  * used by: pci_ctlops() - DDI_CTLOPS_NREGS
677*0Sstevel@tonic-gate  *
678*0Sstevel@tonic-gate  * return value: # of interrupts on success, zero on error
679*0Sstevel@tonic-gate  */
680*0Sstevel@tonic-gate uint_t
681*0Sstevel@tonic-gate get_nintr(dev_info_t *child)
682*0Sstevel@tonic-gate {
683*0Sstevel@tonic-gate 	int *pci_ip;
684*0Sstevel@tonic-gate 	int i, n;
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
687*0Sstevel@tonic-gate 	    "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS)
688*0Sstevel@tonic-gate 		return (0);
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate 	n = i / (int)sizeof (uint_t);
691*0Sstevel@tonic-gate 	kmem_free(pci_ip, i);
692*0Sstevel@tonic-gate 	return (n);
693*0Sstevel@tonic-gate }
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate uint64_t
696*0Sstevel@tonic-gate pci_get_cfg_pabase(pci_t *pci_p)
697*0Sstevel@tonic-gate {
698*0Sstevel@tonic-gate 	int i;
699*0Sstevel@tonic-gate 	pci_ranges_t *rangep = pci_p->pci_ranges;
700*0Sstevel@tonic-gate 	int nrange = pci_p->pci_ranges_length / sizeof (pci_ranges_t);
701*0Sstevel@tonic-gate 	uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG);
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 	ASSERT(cfg_space_type == 0);
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	for (i = 0; i < nrange; i++, rangep++) {
706*0Sstevel@tonic-gate 		if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type)
707*0Sstevel@tonic-gate 			break;
708*0Sstevel@tonic-gate 	}
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	if (i >= nrange)
711*0Sstevel@tonic-gate 		cmn_err(CE_PANIC, "no cfg space in pci(%x) ranges prop.\n",
712*0Sstevel@tonic-gate 			(void *)pci_p);
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 	return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low);
715*0Sstevel@tonic-gate }
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate int
718*0Sstevel@tonic-gate pci_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_errstate_t *pci_err_p,
719*0Sstevel@tonic-gate 	int caller, uint32_t prierr)
720*0Sstevel@tonic-gate {
721*0Sstevel@tonic-gate 	int fatal = 0;
722*0Sstevel@tonic-gate 	int nonfatal = 0;
723*0Sstevel@tonic-gate 	int i;
724*0Sstevel@tonic-gate 	pci_target_err_t tgt_err;
725*0Sstevel@tonic-gate 
726*0Sstevel@tonic-gate 	ASSERT(dip);
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	derr->fme_ena = derr->fme_ena ? derr->fme_ena :
729*0Sstevel@tonic-gate 	    fm_ena_generate(0, FM_ENA_FMT1);
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
732*0Sstevel@tonic-gate 		if (pci_err_p->pci_cfg_stat & pci_err_tbl[i].reg_bit) {
733*0Sstevel@tonic-gate 			char buf[FM_MAX_CLASS];
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
736*0Sstevel@tonic-gate 					PCI_ERROR_SUBCLASS,
737*0Sstevel@tonic-gate 					pci_err_tbl[i].err_class);
738*0Sstevel@tonic-gate 			ddi_fm_ereport_post(dip, buf, derr->fme_ena,
739*0Sstevel@tonic-gate 			    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
740*0Sstevel@tonic-gate 			    PCI_CONFIG_STATUS, DATA_TYPE_UINT16,
741*0Sstevel@tonic-gate 			    pci_err_p->pci_cfg_stat,
742*0Sstevel@tonic-gate 			    PCI_CONFIG_COMMAND, DATA_TYPE_UINT16,
743*0Sstevel@tonic-gate 			    pci_err_p->pci_cfg_comm,
744*0Sstevel@tonic-gate 			    PCI_PA, DATA_TYPE_UINT64,
745*0Sstevel@tonic-gate 			    pci_err_p->pci_pa,
746*0Sstevel@tonic-gate 			    NULL);
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate 			switch (pci_err_tbl[i].reg_bit) {
749*0Sstevel@tonic-gate 			case PCI_STAT_S_SYSERR:
750*0Sstevel@tonic-gate 				/*
751*0Sstevel@tonic-gate 				 * address parity error on dma - treat as fatal
752*0Sstevel@tonic-gate 				 */
753*0Sstevel@tonic-gate 				fatal++;
754*0Sstevel@tonic-gate 				break;
755*0Sstevel@tonic-gate 			case PCI_STAT_R_MAST_AB:
756*0Sstevel@tonic-gate 			case PCI_STAT_R_TARG_AB:
757*0Sstevel@tonic-gate 			case PCI_STAT_S_PERROR:
758*0Sstevel@tonic-gate 				if (prierr) {
759*0Sstevel@tonic-gate 					/*
760*0Sstevel@tonic-gate 					 * piow case are already handled in
761*0Sstevel@tonic-gate 					 * pbm_afsr_report()
762*0Sstevel@tonic-gate 					 */
763*0Sstevel@tonic-gate 					break;
764*0Sstevel@tonic-gate 				}
765*0Sstevel@tonic-gate 				if (caller != PCI_TRAP_CALL) {
766*0Sstevel@tonic-gate 					/*
767*0Sstevel@tonic-gate 					 * if we haven't come from trap handler
768*0Sstevel@tonic-gate 					 * we won't have an address
769*0Sstevel@tonic-gate 					 */
770*0Sstevel@tonic-gate 					fatal++;
771*0Sstevel@tonic-gate 					break;
772*0Sstevel@tonic-gate 				}
773*0Sstevel@tonic-gate 
774*0Sstevel@tonic-gate 				/*
775*0Sstevel@tonic-gate 				 * queue target ereport - use return from
776*0Sstevel@tonic-gate 				 * pci_lookup_handle() to determine if sync
777*0Sstevel@tonic-gate 				 * or async
778*0Sstevel@tonic-gate 				 */
779*0Sstevel@tonic-gate 				tgt_err.tgt_err_ena = derr->fme_ena;
780*0Sstevel@tonic-gate 				tgt_err.tgt_err_class =
781*0Sstevel@tonic-gate 				    pci_err_tbl[i].terr_class;
782*0Sstevel@tonic-gate 				tgt_err.tgt_bridge_type = PCI_ERROR_SUBCLASS;
783*0Sstevel@tonic-gate 				tgt_err.tgt_err_addr =
784*0Sstevel@tonic-gate 				    (uint64_t)derr->fme_bus_specific;
785*0Sstevel@tonic-gate 				nonfatal++;
786*0Sstevel@tonic-gate 				errorq_dispatch(pci_target_queue,
787*0Sstevel@tonic-gate 				    (void *)&tgt_err,
788*0Sstevel@tonic-gate 				    sizeof (pci_target_err_t),
789*0Sstevel@tonic-gate 				    ERRORQ_ASYNC);
790*0Sstevel@tonic-gate 				break;
791*0Sstevel@tonic-gate 			default:
792*0Sstevel@tonic-gate 				/*
793*0Sstevel@tonic-gate 				 * dpe on dma write or ta on dma
794*0Sstevel@tonic-gate 				 */
795*0Sstevel@tonic-gate 				nonfatal++;
796*0Sstevel@tonic-gate 				break;
797*0Sstevel@tonic-gate 			}
798*0Sstevel@tonic-gate 		}
799*0Sstevel@tonic-gate 	}
800*0Sstevel@tonic-gate 
801*0Sstevel@tonic-gate 	if (fatal)
802*0Sstevel@tonic-gate 		return (DDI_FM_FATAL);
803*0Sstevel@tonic-gate 	else if (nonfatal)
804*0Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	return (DDI_FM_OK);
807*0Sstevel@tonic-gate }
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate void
810*0Sstevel@tonic-gate pci_child_cfg_save(dev_info_t *dip)
811*0Sstevel@tonic-gate {
812*0Sstevel@tonic-gate 	dev_info_t *cdip;
813*0Sstevel@tonic-gate 	int ret = DDI_SUCCESS;
814*0Sstevel@tonic-gate 
815*0Sstevel@tonic-gate 	/*
816*0Sstevel@tonic-gate 	 * Save the state of the configuration headers of child
817*0Sstevel@tonic-gate 	 * nodes.
818*0Sstevel@tonic-gate 	 */
819*0Sstevel@tonic-gate 
820*0Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip != NULL;
821*0Sstevel@tonic-gate 	    cdip = ddi_get_next_sibling(cdip)) {
822*0Sstevel@tonic-gate 
823*0Sstevel@tonic-gate 		/*
824*0Sstevel@tonic-gate 		 * Not interested in children who are not already
825*0Sstevel@tonic-gate 		 * init'ed.  They will be set up in init_child().
826*0Sstevel@tonic-gate 		 */
827*0Sstevel@tonic-gate 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
828*0Sstevel@tonic-gate 			DEBUG2(DBG_DETACH, dip, "DDI_SUSPEND: skipping "
829*0Sstevel@tonic-gate 			    "%s%d not in CF1\n", ddi_driver_name(cdip),
830*0Sstevel@tonic-gate 			    ddi_get_instance(cdip));
831*0Sstevel@tonic-gate 
832*0Sstevel@tonic-gate 			continue;
833*0Sstevel@tonic-gate 		}
834*0Sstevel@tonic-gate 
835*0Sstevel@tonic-gate 		/*
836*0Sstevel@tonic-gate 		 * Only save config registers if not already saved by child.
837*0Sstevel@tonic-gate 		 */
838*0Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
839*0Sstevel@tonic-gate 		    SAVED_CONFIG_REGS) == 1) {
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 			continue;
842*0Sstevel@tonic-gate 		}
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate 		/*
845*0Sstevel@tonic-gate 		 * The nexus needs to save config registers.  Create a property
846*0Sstevel@tonic-gate 		 * so it knows to restore on resume.
847*0Sstevel@tonic-gate 		 */
848*0Sstevel@tonic-gate 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
849*0Sstevel@tonic-gate 		    "nexus-saved-config-regs");
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 		if (ret != DDI_PROP_SUCCESS) {
852*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d can't update prop %s",
853*0Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
854*0Sstevel@tonic-gate 			    "nexus-saved-config-regs");
855*0Sstevel@tonic-gate 		}
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 		(void) pci_save_config_regs(cdip);
858*0Sstevel@tonic-gate 	}
859*0Sstevel@tonic-gate }
860*0Sstevel@tonic-gate 
861*0Sstevel@tonic-gate void
862*0Sstevel@tonic-gate pci_child_cfg_restore(dev_info_t *dip)
863*0Sstevel@tonic-gate {
864*0Sstevel@tonic-gate 	dev_info_t *cdip;
865*0Sstevel@tonic-gate 
866*0Sstevel@tonic-gate 	/*
867*0Sstevel@tonic-gate 	 * Restore config registers for children that did not save
868*0Sstevel@tonic-gate 	 * their own registers.  Children pwr states are UNKNOWN after
869*0Sstevel@tonic-gate 	 * a resume since it is possible for the PM framework to call
870*0Sstevel@tonic-gate 	 * resume without an actual power cycle. (ie if suspend fails).
871*0Sstevel@tonic-gate 	 */
872*0Sstevel@tonic-gate 	for (cdip = ddi_get_child(dip); cdip != NULL;
873*0Sstevel@tonic-gate 	    cdip = ddi_get_next_sibling(cdip)) {
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 		/*
876*0Sstevel@tonic-gate 		 * Not interested in children who are not already
877*0Sstevel@tonic-gate 		 * init'ed.  They will be set up by init_child().
878*0Sstevel@tonic-gate 		 */
879*0Sstevel@tonic-gate 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
880*0Sstevel@tonic-gate 			DEBUG2(DBG_DETACH, dip,
881*0Sstevel@tonic-gate 			    "DDI_RESUME: skipping %s%d not in CF1\n",
882*0Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
883*0Sstevel@tonic-gate 			continue;
884*0Sstevel@tonic-gate 		}
885*0Sstevel@tonic-gate 
886*0Sstevel@tonic-gate 		/*
887*0Sstevel@tonic-gate 		 * Only restore config registers if saved by nexus.
888*0Sstevel@tonic-gate 		 */
889*0Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
890*0Sstevel@tonic-gate 		    "nexus-saved-config-regs") == 1) {
891*0Sstevel@tonic-gate 			(void) pci_restore_config_regs(cdip);
892*0Sstevel@tonic-gate 
893*0Sstevel@tonic-gate 			DEBUG2(DBG_PWR, dip,
894*0Sstevel@tonic-gate 			    "DDI_RESUME: nexus restoring %s%d config regs\n",
895*0Sstevel@tonic-gate 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
896*0Sstevel@tonic-gate 
897*0Sstevel@tonic-gate 			if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
898*0Sstevel@tonic-gate 			    "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
899*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "%s%d can't remove prop %s",
900*0Sstevel@tonic-gate 				    ddi_driver_name(cdip),
901*0Sstevel@tonic-gate 				    ddi_get_instance(cdip),
902*0Sstevel@tonic-gate 				    "nexus-saved-config-regs");
903*0Sstevel@tonic-gate 			}
904*0Sstevel@tonic-gate 		}
905*0Sstevel@tonic-gate 	}
906*0Sstevel@tonic-gate }
907