xref: /onnv-gate/usr/src/uts/i86pc/io/pci/pci_common.c (revision 1725:01979e41e5c6)
1881Sjohnny /*
2881Sjohnny  * CDDL HEADER START
3881Sjohnny  *
4881Sjohnny  * The contents of this file are subject to the terms of the
51542Sjohnny  * Common Development and Distribution License (the "License").
61542Sjohnny  * You may not use this file except in compliance with the License.
7881Sjohnny  *
8881Sjohnny  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9881Sjohnny  * or http://www.opensolaris.org/os/licensing.
10881Sjohnny  * See the License for the specific language governing permissions
11881Sjohnny  * and limitations under the License.
12881Sjohnny  *
13881Sjohnny  * When distributing Covered Code, include this CDDL HEADER in each
14881Sjohnny  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15881Sjohnny  * If applicable, add the following below this CDDL HEADER, with the
16881Sjohnny  * fields enclosed by brackets "[]" replaced with your own identifying
17881Sjohnny  * information: Portions Copyright [yyyy] [name of copyright owner]
18881Sjohnny  *
19881Sjohnny  * CDDL HEADER END
20881Sjohnny  */
21881Sjohnny 
22881Sjohnny /*
231542Sjohnny  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24881Sjohnny  * Use is subject to license terms.
25881Sjohnny  */
26881Sjohnny 
27881Sjohnny #pragma ident	"%Z%%M%	%I%	%E% SMI"
28881Sjohnny 
29881Sjohnny /*
30881Sjohnny  *	File that has code which is common between pci(7d) and npe(7d)
31881Sjohnny  *	It shares the following:
32881Sjohnny  *	- interrupt code
33881Sjohnny  *	- pci_tools ioctl code
34881Sjohnny  *	- name_child code
35881Sjohnny  *	- set_parent_private_data code
36881Sjohnny  */
37881Sjohnny 
38881Sjohnny #include <sys/conf.h>
39881Sjohnny #include <sys/pci.h>
40881Sjohnny #include <sys/sunndi.h>
41916Sschwartz #include <sys/mach_intr.h>
42881Sjohnny #include <sys/hotplug/pci/pcihp.h>
43881Sjohnny #include <sys/pci_intr_lib.h>
44881Sjohnny #include <sys/psm.h>
45881Sjohnny #include <sys/policy.h>
46881Sjohnny #include <sys/sysmacros.h>
47916Sschwartz #include <sys/clock.h>
48916Sschwartz #include <io/pcplusmp/apic.h>
49881Sjohnny #include <sys/pci_tools.h>
50881Sjohnny #include <io/pci/pci_var.h>
51881Sjohnny #include <io/pci/pci_tools_ext.h>
52881Sjohnny #include <io/pci/pci_common.h>
531083Sanish #include <sys/pci_cfgspace.h>
541083Sanish #include <sys/pci_impl.h>
55881Sjohnny 
56881Sjohnny /*
57881Sjohnny  * Function prototypes
58881Sjohnny  */
59881Sjohnny static int	pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *);
60881Sjohnny static int	pci_get_nintrs(dev_info_t *, int, int *);
61881Sjohnny static int	pci_enable_intr(dev_info_t *, dev_info_t *,
62881Sjohnny 		    ddi_intr_handle_impl_t *, uint32_t);
63881Sjohnny static void	pci_disable_intr(dev_info_t *, dev_info_t *,
64881Sjohnny 		    ddi_intr_handle_impl_t *, uint32_t);
65881Sjohnny 
66881Sjohnny /* Extern decalration for pcplusmp module */
67881Sjohnny extern int	(*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
68881Sjohnny 		    psm_intr_op_t, int *);
69881Sjohnny 
70881Sjohnny 
71881Sjohnny /*
72881Sjohnny  * pci_name_child:
73881Sjohnny  *
74881Sjohnny  *	Assign the address portion of the node name
75881Sjohnny  */
76881Sjohnny int
77881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen)
78881Sjohnny {
79881Sjohnny 	int		dev, func, length;
80881Sjohnny 	char		**unit_addr;
81881Sjohnny 	uint_t		n;
82881Sjohnny 	pci_regspec_t	*pci_rp;
83881Sjohnny 
84881Sjohnny 	if (ndi_dev_is_persistent_node(child) == 0) {
85881Sjohnny 		/*
86881Sjohnny 		 * For .conf node, use "unit-address" property
87881Sjohnny 		 */
88881Sjohnny 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
89881Sjohnny 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
90881Sjohnny 		    DDI_PROP_SUCCESS) {
91881Sjohnny 			cmn_err(CE_WARN, "cannot find unit-address in %s.conf",
92881Sjohnny 			    ddi_get_name(child));
93881Sjohnny 			return (DDI_FAILURE);
94881Sjohnny 		}
95881Sjohnny 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
96881Sjohnny 			cmn_err(CE_WARN, "unit-address property in %s.conf"
97881Sjohnny 			    " not well-formed", ddi_get_name(child));
98881Sjohnny 			ddi_prop_free(unit_addr);
99881Sjohnny 			return (DDI_FAILURE);
100881Sjohnny 		}
101881Sjohnny 		(void) snprintf(name, namelen, "%s", *unit_addr);
102881Sjohnny 		ddi_prop_free(unit_addr);
103881Sjohnny 		return (DDI_SUCCESS);
104881Sjohnny 	}
105881Sjohnny 
106881Sjohnny 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
107881Sjohnny 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
108881Sjohnny 		cmn_err(CE_WARN, "cannot find reg property in %s",
109881Sjohnny 		    ddi_get_name(child));
110881Sjohnny 		return (DDI_FAILURE);
111881Sjohnny 	}
112881Sjohnny 
113881Sjohnny 	/* copy the device identifications */
114881Sjohnny 	dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
115881Sjohnny 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
116881Sjohnny 
117881Sjohnny 	/*
118881Sjohnny 	 * free the memory allocated by ddi_prop_lookup_int_array
119881Sjohnny 	 */
120881Sjohnny 	ddi_prop_free(pci_rp);
121881Sjohnny 
122881Sjohnny 	if (func != 0) {
123881Sjohnny 		(void) snprintf(name, namelen, "%x,%x", dev, func);
124881Sjohnny 	} else {
125881Sjohnny 		(void) snprintf(name, namelen, "%x", dev);
126881Sjohnny 	}
127881Sjohnny 
128881Sjohnny 	return (DDI_SUCCESS);
129881Sjohnny }
130881Sjohnny 
131881Sjohnny /*
132881Sjohnny  * Interrupt related code:
133881Sjohnny  *
134881Sjohnny  * The following busop is common to npe and pci drivers
135881Sjohnny  *	bus_introp
136881Sjohnny  */
137881Sjohnny 
138881Sjohnny /*
139881Sjohnny  * Create the ddi_parent_private_data for a pseudo child.
140881Sjohnny  */
141881Sjohnny void
142881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip)
143881Sjohnny {
144881Sjohnny 	struct ddi_parent_private_data *pdptr;
145881Sjohnny 
146881Sjohnny 	pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
147881Sjohnny 	    (sizeof (struct ddi_parent_private_data) +
148881Sjohnny 	sizeof (struct intrspec)), KM_SLEEP);
149881Sjohnny 	pdptr->par_intr = (struct intrspec *)(pdptr + 1);
150881Sjohnny 	pdptr->par_nintr = 1;
151881Sjohnny 	ddi_set_parent_data(dip, pdptr);
152881Sjohnny }
153881Sjohnny 
154881Sjohnny /*
155881Sjohnny  * pci_get_priority:
156881Sjohnny  *	Figure out the priority of the device
157881Sjohnny  */
158881Sjohnny static int
159881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri)
160881Sjohnny {
161881Sjohnny 	struct intrspec *ispec;
162881Sjohnny 
163881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n",
164881Sjohnny 	    (void *)dip, (void *)hdlp));
165881Sjohnny 
166881Sjohnny 	if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
167881Sjohnny 	    hdlp->ih_inum)) == NULL) {
168881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) {
169881Sjohnny 			int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
170881Sjohnny 			    DDI_PROP_DONTPASS, "class-code", -1);
171881Sjohnny 
172881Sjohnny 			*pri = (class == -1) ? 1 : pci_devclass_to_ipl(class);
173881Sjohnny 			pci_common_set_parent_private_data(hdlp->ih_dip);
174881Sjohnny 			ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
175881Sjohnny 			    hdlp->ih_inum);
176881Sjohnny 			return (DDI_SUCCESS);
177881Sjohnny 		}
178881Sjohnny 		return (DDI_FAILURE);
179881Sjohnny 	}
180881Sjohnny 
181881Sjohnny 	*pri = ispec->intrspec_pri;
182881Sjohnny 	return (DDI_SUCCESS);
183881Sjohnny }
184881Sjohnny 
185881Sjohnny 
186881Sjohnny /*
187881Sjohnny  * pci_get_nintrs:
188881Sjohnny  *	Figure out how many interrupts the device supports
189881Sjohnny  */
190881Sjohnny static int
191881Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
192881Sjohnny {
193881Sjohnny 	int	ret;
194881Sjohnny 
195881Sjohnny 	*nintrs = 0;
196881Sjohnny 
197881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(type))
198881Sjohnny 		ret = pci_msi_get_nintrs(dip, type, nintrs);
199881Sjohnny 	else {
200881Sjohnny 		ret = DDI_FAILURE;
201881Sjohnny 		if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
202881Sjohnny 		    "interrupts", -1) != -1) {
203881Sjohnny 			*nintrs = 1;
204881Sjohnny 			ret = DDI_SUCCESS;
205881Sjohnny 		}
206881Sjohnny 	}
207881Sjohnny 
208881Sjohnny 	return (ret);
209881Sjohnny }
210881Sjohnny 
211881Sjohnny static int pcie_pci_intr_pri_counter = 0;
212881Sjohnny 
213881Sjohnny /*
214881Sjohnny  * pci_common_intr_ops: bus_intr_op() function for interrupt support
215881Sjohnny  */
216881Sjohnny int
217881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
218881Sjohnny     ddi_intr_handle_impl_t *hdlp, void *result)
219881Sjohnny {
220881Sjohnny 	int			priority = 0;
221881Sjohnny 	int			psm_status = 0;
222881Sjohnny 	int			pci_status = 0;
223881Sjohnny 	int			pci_rval, psm_rval = PSM_FAILURE;
224881Sjohnny 	int			types = 0;
225881Sjohnny 	int			pciepci = 0;
2261542Sjohnny 	int			i, j, count;
227881Sjohnny 	int			behavior;
228881Sjohnny 	ddi_intrspec_t		isp;
229881Sjohnny 	struct intrspec		*ispec;
230881Sjohnny 	ddi_intr_handle_impl_t	tmp_hdl;
231881Sjohnny 	ddi_intr_msix_t		*msix_p;
2321087Sschwartz 	ihdl_plat_t		*ihdl_plat_datap;
2331542Sjohnny 	ddi_intr_handle_t	*h_array;
234881Sjohnny 
235881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT,
236881Sjohnny 	    "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
237881Sjohnny 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
238881Sjohnny 
239881Sjohnny 	/* Process the request */
240881Sjohnny 	switch (intr_op) {
241881Sjohnny 	case DDI_INTROP_SUPPORTED_TYPES:
242881Sjohnny 		/* Fixed supported by default */
243881Sjohnny 		*(int *)result = DDI_INTR_TYPE_FIXED;
244881Sjohnny 
245881Sjohnny 		/* Figure out if MSI or MSI-X is supported? */
246881Sjohnny 		if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
247881Sjohnny 			return (DDI_SUCCESS);
248881Sjohnny 
249881Sjohnny 		if (psm_intr_ops != NULL) {
250*1725Segillett 			/*
251*1725Segillett 			 * Only support MSI for now, OR it in
252*1725Segillett 			 */
253*1725Segillett 			*(int *)result |= (types & DDI_INTR_TYPE_MSI);
254881Sjohnny 
255881Sjohnny 			tmp_hdl.ih_type = *(int *)result;
256881Sjohnny 			(void) (*psm_intr_ops)(rdip, &tmp_hdl,
257881Sjohnny 			    PSM_INTR_OP_CHECK_MSI, result);
258881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
259881Sjohnny 			    "rdip: 0x%p supported types: 0x%x\n", (void *)rdip,
260881Sjohnny 			    *(int *)result));
261881Sjohnny 		}
262881Sjohnny 		break;
263881Sjohnny 	case DDI_INTROP_NINTRS:
264881Sjohnny 		if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
265881Sjohnny 			return (DDI_FAILURE);
266881Sjohnny 		break;
267881Sjohnny 	case DDI_INTROP_ALLOC:
268881Sjohnny 		/*
269881Sjohnny 		 * MSI or MSIX (figure out number of vectors available)
270881Sjohnny 		 * FIXED interrupts: just return available interrupts
271881Sjohnny 		 */
272881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
273881Sjohnny 		    (psm_intr_ops != NULL) &&
274881Sjohnny 		    (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) {
275881Sjohnny 			/*
276881Sjohnny 			 * Following check is a special case for 'pcie_pci'.
277881Sjohnny 			 * This makes sure vectors with the right priority
278881Sjohnny 			 * are allocated for pcie_pci during ALLOC time.
279881Sjohnny 			 */
280881Sjohnny 			if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) {
281881Sjohnny 				hdlp->ih_pri =
282881Sjohnny 				    (pcie_pci_intr_pri_counter % 2) ? 4 : 7;
283881Sjohnny 				pciepci = 1;
284881Sjohnny 			} else
285881Sjohnny 				hdlp->ih_pri = priority;
2861717Swesolows 			behavior = (int)(uintptr_t)hdlp->ih_scratch2;
287881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
288881Sjohnny 			    PSM_INTR_OP_ALLOC_VECTORS, result);
289881Sjohnny 
290881Sjohnny 			/* verify behavior flag and take appropriate action */
291881Sjohnny 			if ((behavior == DDI_INTR_ALLOC_STRICT) &&
292881Sjohnny 			    (*(int *)result < hdlp->ih_scratch1)) {
293881Sjohnny 				DDI_INTR_NEXDBG((CE_CONT,
294881Sjohnny 				    "pci_common_intr_ops: behavior %x, "
295881Sjohnny 				    "couldn't get enough intrs\n", behavior));
296881Sjohnny 				hdlp->ih_scratch1 = *(int *)result;
297881Sjohnny 				(void) (*psm_intr_ops)(rdip, hdlp,
298881Sjohnny 				    PSM_INTR_OP_FREE_VECTORS, NULL);
299881Sjohnny 				return (DDI_EAGAIN);
300881Sjohnny 			}
301881Sjohnny 
302881Sjohnny 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
303881Sjohnny 				if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
304881Sjohnny 					msix_p = pci_msix_init(hdlp->ih_dip);
305881Sjohnny 					if (msix_p)
306881Sjohnny 						i_ddi_set_msix(hdlp->ih_dip,
307881Sjohnny 						    msix_p);
308881Sjohnny 				}
309881Sjohnny 			}
310881Sjohnny 
311881Sjohnny 			if (pciepci) {
312881Sjohnny 				/* update priority in ispec */
313881Sjohnny 				isp = pci_intx_get_ispec(pdip, rdip,
314881Sjohnny 					(int)hdlp->ih_inum);
315881Sjohnny 				ispec = (struct intrspec *)isp;
316881Sjohnny 				if (ispec)
317881Sjohnny 					ispec->intrspec_pri = hdlp->ih_pri;
318881Sjohnny 				++pcie_pci_intr_pri_counter;
319881Sjohnny 			}
320881Sjohnny 
321881Sjohnny 		} else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
322881Sjohnny 			/* Figure out if this device supports MASKING */
323881Sjohnny 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
324881Sjohnny 			if (pci_rval == DDI_SUCCESS && pci_status)
325881Sjohnny 				hdlp->ih_cap |= pci_status;
326881Sjohnny 			*(int *)result = 1;	/* DDI_INTR_TYPE_FIXED */
327881Sjohnny 		} else
328881Sjohnny 			return (DDI_FAILURE);
329881Sjohnny 		break;
330881Sjohnny 	case DDI_INTROP_FREE:
331881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
332881Sjohnny 		    (psm_intr_ops != NULL)) {
333881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
334881Sjohnny 			    PSM_INTR_OP_FREE_VECTORS, NULL);
335881Sjohnny 
336881Sjohnny 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
337881Sjohnny 				msix_p = i_ddi_get_msix(hdlp->ih_dip);
338881Sjohnny 				if (msix_p &&
339*1725Segillett 				    i_ddi_intr_get_current_nintrs(hdlp->ih_dip)
340*1725Segillett 				    == 0) {
341881Sjohnny 					pci_msix_fini(msix_p);
342881Sjohnny 					i_ddi_set_msix(hdlp->ih_dip, NULL);
343881Sjohnny 				}
344881Sjohnny 			}
345881Sjohnny 		}
346881Sjohnny 		break;
347881Sjohnny 	case DDI_INTROP_GETPRI:
348881Sjohnny 		/* Get the priority */
349881Sjohnny 		if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS)
350881Sjohnny 			return (DDI_FAILURE);
351881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
352881Sjohnny 		    "priority = 0x%x\n", priority));
353881Sjohnny 		*(int *)result = priority;
354881Sjohnny 		break;
355881Sjohnny 	case DDI_INTROP_SETPRI:
356881Sjohnny 		/* Validate the interrupt priority passed */
357881Sjohnny 		if (*(int *)result > LOCK_LEVEL)
358881Sjohnny 			return (DDI_FAILURE);
359881Sjohnny 
360881Sjohnny 		/* Ensure that PSM is all initialized */
361881Sjohnny 		if (psm_intr_ops == NULL)
362881Sjohnny 			return (DDI_FAILURE);
363881Sjohnny 
364881Sjohnny 		/* Change the priority */
365881Sjohnny 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
366881Sjohnny 		    PSM_FAILURE)
367881Sjohnny 			return (DDI_FAILURE);
368881Sjohnny 
369881Sjohnny 		/* update ispec */
370881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
371881Sjohnny 		ispec = (struct intrspec *)isp;
372881Sjohnny 		if (ispec)
373881Sjohnny 			ispec->intrspec_pri = *(int *)result;
374881Sjohnny 		break;
375881Sjohnny 	case DDI_INTROP_ADDISR:
376881Sjohnny 		/* update ispec */
377881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
378881Sjohnny 		ispec = (struct intrspec *)isp;
3791087Sschwartz 		if (ispec) {
380881Sjohnny 			ispec->intrspec_func = hdlp->ih_cb_func;
3811087Sschwartz 			ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
3821087Sschwartz 			pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp);
3831087Sschwartz 		}
384881Sjohnny 		break;
385881Sjohnny 	case DDI_INTROP_REMISR:
386881Sjohnny 		/* Get the interrupt structure pointer */
387881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
388881Sjohnny 		ispec = (struct intrspec *)isp;
3891087Sschwartz 		if (ispec) {
390881Sjohnny 			ispec->intrspec_func = (uint_t (*)()) 0;
3911087Sschwartz 			ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
3921087Sschwartz 			if (ihdl_plat_datap->ip_ksp != NULL)
3931087Sschwartz 				pci_kstat_delete(ihdl_plat_datap->ip_ksp);
3941087Sschwartz 		}
395881Sjohnny 		break;
396881Sjohnny 	case DDI_INTROP_GETCAP:
397881Sjohnny 		/*
398881Sjohnny 		 * First check the config space and/or
399881Sjohnny 		 * MSI capability register(s)
400881Sjohnny 		 */
401881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
402881Sjohnny 			pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
403881Sjohnny 			    &pci_status);
404881Sjohnny 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
405881Sjohnny 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
406881Sjohnny 
407881Sjohnny 		/* next check with pcplusmp */
408881Sjohnny 		if (psm_intr_ops != NULL)
409881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
410881Sjohnny 			    PSM_INTR_OP_GET_CAP, &psm_status);
411881Sjohnny 
412881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
413881Sjohnny 		    "psm_status = %x, pci_rval = %x, pci_status = %x\n",
414881Sjohnny 		    psm_rval, psm_status, pci_rval, pci_status));
415881Sjohnny 
416881Sjohnny 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
417881Sjohnny 			*(int *)result = 0;
418881Sjohnny 			return (DDI_FAILURE);
419881Sjohnny 		}
420881Sjohnny 
421881Sjohnny 		if (psm_rval == PSM_SUCCESS)
422881Sjohnny 			*(int *)result = psm_status;
423881Sjohnny 
424881Sjohnny 		if (pci_rval == DDI_SUCCESS)
425881Sjohnny 			*(int *)result |= pci_status;
426881Sjohnny 
427881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
428881Sjohnny 		    *(int *)result));
429881Sjohnny 		break;
430881Sjohnny 	case DDI_INTROP_SETCAP:
431881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
432881Sjohnny 		    "SETCAP cap=0x%x\n", *(int *)result));
433881Sjohnny 		if (psm_intr_ops == NULL)
434881Sjohnny 			return (DDI_FAILURE);
435881Sjohnny 
436881Sjohnny 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
437881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
438881Sjohnny 			    " returned failure\n"));
439881Sjohnny 			return (DDI_FAILURE);
440881Sjohnny 		}
441881Sjohnny 		break;
442881Sjohnny 	case DDI_INTROP_ENABLE:
443881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n"));
444881Sjohnny 		if (psm_intr_ops == NULL)
445881Sjohnny 			return (DDI_FAILURE);
446881Sjohnny 
447881Sjohnny 		if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
448881Sjohnny 		    DDI_SUCCESS)
449881Sjohnny 			return (DDI_FAILURE);
450881Sjohnny 
451881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE "
452881Sjohnny 		    "vector=0x%x\n", hdlp->ih_vector));
453881Sjohnny 		break;
454881Sjohnny 	case DDI_INTROP_DISABLE:
455881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n"));
456881Sjohnny 		if (psm_intr_ops == NULL)
457881Sjohnny 			return (DDI_FAILURE);
458881Sjohnny 
459881Sjohnny 		pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
460881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE "
461881Sjohnny 		    "vector = %x\n", hdlp->ih_vector));
462881Sjohnny 		break;
463881Sjohnny 	case DDI_INTROP_BLOCKENABLE:
464881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
465881Sjohnny 		    "BLOCKENABLE\n"));
466881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
467881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
468881Sjohnny 			return (DDI_FAILURE);
469881Sjohnny 		}
470881Sjohnny 
471881Sjohnny 		/* Check if psm_intr_ops is NULL? */
472881Sjohnny 		if (psm_intr_ops == NULL)
473881Sjohnny 			return (DDI_FAILURE);
474881Sjohnny 
4751542Sjohnny 		count = hdlp->ih_scratch1;
4761542Sjohnny 		h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2;
4771542Sjohnny 		for (i = 0; i < count; i++) {
4781542Sjohnny 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
479881Sjohnny 			if (pci_enable_intr(pdip, rdip, hdlp,
4801542Sjohnny 			    hdlp->ih_inum) != DDI_SUCCESS) {
481881Sjohnny 				DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
482881Sjohnny 				    "pci_enable_intr failed for %d\n", i));
4831542Sjohnny 				for (j = 0; j < i; j++) {
4841542Sjohnny 				    hdlp = (ddi_intr_handle_impl_t *)h_array[j];
4851542Sjohnny 				    pci_disable_intr(pdip, rdip, hdlp,
4861542Sjohnny 					    hdlp->ih_inum);
4871542Sjohnny 				}
488881Sjohnny 				return (DDI_FAILURE);
489881Sjohnny 			}
490881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
4911542Sjohnny 			    "BLOCKENABLE inum %x done\n", hdlp->ih_inum));
492881Sjohnny 		}
493881Sjohnny 		break;
494881Sjohnny 	case DDI_INTROP_BLOCKDISABLE:
495881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
496881Sjohnny 		    "BLOCKDISABLE\n"));
497881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
498881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
499881Sjohnny 			return (DDI_FAILURE);
500881Sjohnny 		}
501881Sjohnny 
502881Sjohnny 		/* Check if psm_intr_ops is present */
503881Sjohnny 		if (psm_intr_ops == NULL)
504881Sjohnny 			return (DDI_FAILURE);
505881Sjohnny 
5061542Sjohnny 		count = hdlp->ih_scratch1;
5071542Sjohnny 		h_array = (ddi_intr_handle_t *)hdlp->ih_scratch2;
5081542Sjohnny 		for (i = 0; i < count; i++) {
5091542Sjohnny 			hdlp = (ddi_intr_handle_impl_t *)h_array[i];
5101542Sjohnny 			pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
511881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
5121542Sjohnny 			    "BLOCKDISABLE inum %x done\n", hdlp->ih_inum));
513881Sjohnny 		}
514881Sjohnny 		break;
515881Sjohnny 	case DDI_INTROP_SETMASK:
516881Sjohnny 	case DDI_INTROP_CLRMASK:
517881Sjohnny 		/*
518881Sjohnny 		 * First handle in the config space
519881Sjohnny 		 */
520881Sjohnny 		if (intr_op == DDI_INTROP_SETMASK) {
521881Sjohnny 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
522881Sjohnny 				pci_status = pci_msi_set_mask(rdip,
523881Sjohnny 				    hdlp->ih_type, hdlp->ih_inum);
524881Sjohnny 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
525881Sjohnny 				pci_status = pci_intx_set_mask(rdip);
526881Sjohnny 		} else {
527881Sjohnny 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
528881Sjohnny 				pci_status = pci_msi_clr_mask(rdip,
529881Sjohnny 				    hdlp->ih_type, hdlp->ih_inum);
530881Sjohnny 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
531881Sjohnny 				pci_status = pci_intx_clr_mask(rdip);
532881Sjohnny 		}
533881Sjohnny 
534881Sjohnny 		/* For MSI/X; no need to check with pcplusmp */
535881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
536881Sjohnny 			return (pci_status);
537881Sjohnny 
538881Sjohnny 		/* For fixed interrupts only: handle config space first */
539881Sjohnny 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
540881Sjohnny 		    pci_status == DDI_SUCCESS)
541881Sjohnny 			break;
542881Sjohnny 
543881Sjohnny 		/* For fixed interrupts only: confer with pcplusmp next */
544881Sjohnny 		if (psm_intr_ops != NULL) {
545881Sjohnny 			/* If interrupt is shared; do nothing */
546881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
547881Sjohnny 			    PSM_INTR_OP_GET_SHARED, &psm_status);
548881Sjohnny 
549881Sjohnny 			if (psm_rval == PSM_FAILURE || psm_status == 1)
550881Sjohnny 				return (pci_status);
551881Sjohnny 
552881Sjohnny 			/* Now, pcplusmp should try to set/clear the mask */
553881Sjohnny 			if (intr_op == DDI_INTROP_SETMASK)
554881Sjohnny 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
555881Sjohnny 				    PSM_INTR_OP_SET_MASK, NULL);
556881Sjohnny 			else
557881Sjohnny 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
558881Sjohnny 				    PSM_INTR_OP_CLEAR_MASK, NULL);
559881Sjohnny 		}
560881Sjohnny 		return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
561881Sjohnny 	case DDI_INTROP_GETPENDING:
562881Sjohnny 		/*
563881Sjohnny 		 * First check the config space and/or
564881Sjohnny 		 * MSI capability register(s)
565881Sjohnny 		 */
566881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
567881Sjohnny 			pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
568881Sjohnny 			    hdlp->ih_inum, &pci_status);
569881Sjohnny 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
570881Sjohnny 			pci_rval = pci_intx_get_pending(rdip, &pci_status);
571881Sjohnny 
572881Sjohnny 		/* On failure; next try with pcplusmp */
573881Sjohnny 		if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
574881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
575881Sjohnny 			    PSM_INTR_OP_GET_PENDING, &psm_status);
576881Sjohnny 
577881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
578881Sjohnny 		    "psm_rval = %x, psm_status = %x, pci_rval = %x, "
579881Sjohnny 		    "pci_status = %x\n", psm_rval, psm_status, pci_rval,
580881Sjohnny 		    pci_status));
581881Sjohnny 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
582881Sjohnny 			*(int *)result = 0;
583881Sjohnny 			return (DDI_FAILURE);
584881Sjohnny 		}
585881Sjohnny 
586881Sjohnny 		if (psm_rval != PSM_FAILURE)
587881Sjohnny 			*(int *)result = psm_status;
588881Sjohnny 		else if (pci_rval != DDI_FAILURE)
589881Sjohnny 			*(int *)result = pci_status;
590881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
591881Sjohnny 		    *(int *)result));
592881Sjohnny 		break;
593881Sjohnny 	case DDI_INTROP_NAVAIL:
594881Sjohnny 		if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
595881Sjohnny 		    hdlp, &priority) == DDI_SUCCESS)) {
596881Sjohnny 			/* Priority in the handle not initialized yet */
597881Sjohnny 			hdlp->ih_pri = priority;
598881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
599881Sjohnny 			    PSM_INTR_OP_NAVAIL_VECTORS, result);
600881Sjohnny 		} else {
601881Sjohnny 			*(int *)result = 1;
602881Sjohnny 		}
603881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
604881Sjohnny 		    *(int *)result));
605881Sjohnny 		break;
606881Sjohnny 	default:
607881Sjohnny 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
608881Sjohnny 	}
609881Sjohnny 
610881Sjohnny 	return (DDI_SUCCESS);
611881Sjohnny }
612881Sjohnny 
613916Sschwartz int
614916Sschwartz pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p,
615916Sschwartz     int vecirq, boolean_t is_irq)
616916Sschwartz {
617916Sschwartz 	ddi_intr_handle_impl_t	get_info_ii_hdl;
618916Sschwartz 
619916Sschwartz 	if (is_irq)
620916Sschwartz 		intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ;
621916Sschwartz 
622916Sschwartz 	/*
623916Sschwartz 	 * For this locally-declared and used handle, ih_private will contain a
624916Sschwartz 	 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for
625916Sschwartz 	 * global interrupt handling.
626916Sschwartz 	 */
627916Sschwartz 	get_info_ii_hdl.ih_private = intrinfo_p;
628916Sschwartz 	get_info_ii_hdl.ih_vector = (ushort_t)vecirq;
629916Sschwartz 
630916Sschwartz 	if ((*psm_intr_ops)(NULL, &get_info_ii_hdl,
631916Sschwartz 	    PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE)
632916Sschwartz 		return (DDI_FAILURE);
633916Sschwartz 
634916Sschwartz 	return (DDI_SUCCESS);
635916Sschwartz }
636916Sschwartz 
637916Sschwartz 
638916Sschwartz int
639916Sschwartz pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq)
640916Sschwartz {
641916Sschwartz 	int rval;
642916Sschwartz 
643916Sschwartz 	apic_get_intr_t	intrinfo;
644916Sschwartz 	intrinfo.avgi_req_flags = PSMGI_REQ_CPUID;
645916Sschwartz 	rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq);
646916Sschwartz 
647916Sschwartz 	if (rval == DDI_SUCCESS)
648916Sschwartz 		return (intrinfo.avgi_cpu_id);
649916Sschwartz 	else
650916Sschwartz 		return (-1);
651916Sschwartz }
652916Sschwartz 
653881Sjohnny 
654881Sjohnny static int
655881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
656881Sjohnny     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
657881Sjohnny {
658881Sjohnny 	struct intrspec	*ispec;
659916Sschwartz 	int		irq;
660916Sschwartz 	ihdl_plat_t	*ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
661881Sjohnny 
662881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
663881Sjohnny 	    (void *)hdlp, inum));
664881Sjohnny 
665881Sjohnny 	/* Translate the interrupt if needed */
666881Sjohnny 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
667881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
668881Sjohnny 		ispec->intrspec_vec = inum;
669916Sschwartz 	ihdl_plat_datap->ip_ispecp = ispec;
670881Sjohnny 
671881Sjohnny 	/* translate the interrupt if needed */
672916Sschwartz 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq);
673916Sschwartz 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n",
674916Sschwartz 	    hdlp->ih_pri, irq));
675881Sjohnny 
676881Sjohnny 	/* Add the interrupt handler */
677881Sjohnny 	if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
678916Sschwartz 	    DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1,
679916Sschwartz 	    hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip))
680881Sjohnny 		return (DDI_FAILURE);
681881Sjohnny 
682916Sschwartz 	/* Note this really is an irq. */
683916Sschwartz 	hdlp->ih_vector = (ushort_t)irq;
684916Sschwartz 
685881Sjohnny 	return (DDI_SUCCESS);
686881Sjohnny }
687881Sjohnny 
688881Sjohnny 
689881Sjohnny static void
690881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
691881Sjohnny     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
692881Sjohnny {
693916Sschwartz 	int		irq;
694881Sjohnny 	struct intrspec	*ispec;
695916Sschwartz 	ihdl_plat_t	*ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
696881Sjohnny 
697881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
698881Sjohnny 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
699881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
700881Sjohnny 		ispec->intrspec_vec = inum;
701916Sschwartz 	ihdl_plat_datap->ip_ispecp = ispec;
702881Sjohnny 
703881Sjohnny 	/* translate the interrupt if needed */
704916Sschwartz 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq);
705881Sjohnny 
706881Sjohnny 	/* Disable the interrupt handler */
707916Sschwartz 	rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, irq);
708916Sschwartz 	ihdl_plat_datap->ip_ispecp = NULL;
709881Sjohnny }
710881Sjohnny 
711881Sjohnny /*
712881Sjohnny  * Miscellaneous library function
713881Sjohnny  */
714881Sjohnny int
715881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
716881Sjohnny {
717881Sjohnny 	int		i;
718881Sjohnny 	int 		number;
719881Sjohnny 	int		assigned_addr_len;
720881Sjohnny 	uint_t		phys_hi = pci_rp->pci_phys_hi;
721881Sjohnny 	pci_regspec_t	*assigned_addr;
722881Sjohnny 
723881Sjohnny 	if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) ||
724881Sjohnny 	    (phys_hi & PCI_RELOCAT_B))
725881Sjohnny 		return (DDI_SUCCESS);
726881Sjohnny 
727881Sjohnny 	/*
728881Sjohnny 	 * the "reg" property specifies relocatable, get and interpret the
729881Sjohnny 	 * "assigned-addresses" property.
730881Sjohnny 	 */
731881Sjohnny 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
732881Sjohnny 	    "assigned-addresses", (int **)&assigned_addr,
733881Sjohnny 	    (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS)
734881Sjohnny 		return (DDI_FAILURE);
735881Sjohnny 
736881Sjohnny 	/*
737881Sjohnny 	 * Scan the "assigned-addresses" for one that matches the specified
738881Sjohnny 	 * "reg" property entry.
739881Sjohnny 	 */
740881Sjohnny 	phys_hi &= PCI_CONF_ADDR_MASK;
741881Sjohnny 	number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int));
742881Sjohnny 	for (i = 0; i < number; i++) {
743881Sjohnny 		if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
744881Sjohnny 		    phys_hi) {
745881Sjohnny 			pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
746881Sjohnny 			pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
747881Sjohnny 			ddi_prop_free(assigned_addr);
748881Sjohnny 			return (DDI_SUCCESS);
749881Sjohnny 		}
750881Sjohnny 	}
751881Sjohnny 
752881Sjohnny 	ddi_prop_free(assigned_addr);
753881Sjohnny 	return (DDI_FAILURE);
754881Sjohnny }
755881Sjohnny 
756881Sjohnny 
757881Sjohnny /*
758881Sjohnny  * For pci_tools
759881Sjohnny  */
760881Sjohnny 
761881Sjohnny int
762881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
763881Sjohnny     int mode, cred_t *credp, int *rvalp)
764881Sjohnny {
765881Sjohnny 	int rv = ENOTTY;
766881Sjohnny 
767881Sjohnny 	minor_t minor = getminor(dev);
768881Sjohnny 
769881Sjohnny 	switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
770881Sjohnny 	case PCI_TOOL_REG_MINOR_NUM:
771881Sjohnny 
772881Sjohnny 		switch (cmd) {
773881Sjohnny 		case PCITOOL_DEVICE_SET_REG:
774881Sjohnny 		case PCITOOL_DEVICE_GET_REG:
775881Sjohnny 
776881Sjohnny 			/* Require full privileges. */
777881Sjohnny 			if (secpolicy_kmdb(credp))
778881Sjohnny 				rv = EPERM;
779881Sjohnny 			else
780881Sjohnny 				rv = pcitool_dev_reg_ops(dip, (void *)arg,
781881Sjohnny 				    cmd, mode);
782881Sjohnny 			break;
783881Sjohnny 
784881Sjohnny 		case PCITOOL_NEXUS_SET_REG:
785881Sjohnny 		case PCITOOL_NEXUS_GET_REG:
786881Sjohnny 
787881Sjohnny 			/* Require full privileges. */
788881Sjohnny 			if (secpolicy_kmdb(credp))
789881Sjohnny 				rv = EPERM;
790881Sjohnny 			else
791881Sjohnny 				rv = pcitool_bus_reg_ops(dip, (void *)arg,
792881Sjohnny 				    cmd, mode);
793881Sjohnny 			break;
794881Sjohnny 		}
795881Sjohnny 		break;
796881Sjohnny 
797881Sjohnny 	case PCI_TOOL_INTR_MINOR_NUM:
798881Sjohnny 
799881Sjohnny 		switch (cmd) {
800881Sjohnny 		case PCITOOL_DEVICE_SET_INTR:
801881Sjohnny 
802881Sjohnny 			/* Require PRIV_SYS_RES_CONFIG, same as psradm */
803881Sjohnny 			if (secpolicy_ponline(credp)) {
804881Sjohnny 				rv = EPERM;
805881Sjohnny 				break;
806881Sjohnny 			}
807881Sjohnny 
808881Sjohnny 		/*FALLTHRU*/
809881Sjohnny 		/* These require no special privileges. */
810881Sjohnny 		case PCITOOL_DEVICE_GET_INTR:
811881Sjohnny 		case PCITOOL_DEVICE_NUM_INTR:
812881Sjohnny 			rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode);
813881Sjohnny 			break;
814881Sjohnny 		}
815881Sjohnny 		break;
816881Sjohnny 
817881Sjohnny 	/*
818881Sjohnny 	 * All non-PCItool ioctls go through here, including:
819881Sjohnny 	 *   devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
820881Sjohnny 	 *   those for attachment points with where minor number is the
821881Sjohnny 	 *   device number.
822881Sjohnny 	 */
823881Sjohnny 	default:
824881Sjohnny 		rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
825881Sjohnny 		    credp, rvalp);
826881Sjohnny 		break;
827881Sjohnny 	}
828881Sjohnny 
829881Sjohnny 	return (rv);
830881Sjohnny }
8311083Sanish 
8321083Sanish 
8331083Sanish /*
8341083Sanish  * These are the get and put functions to be shared with drivers. The
8351083Sanish  * mutex locking is done inside the functions referenced, rather than
8361083Sanish  * here, and is thus shared across PCI child drivers and any other
8371083Sanish  * consumers of PCI config space (such as the ACPI subsystem).
8381083Sanish  *
8391083Sanish  * The configuration space addresses come in as pointers.  This is fine on
8401083Sanish  * a 32-bit system, where the VM space and configuration space are the same
8411083Sanish  * size.  It's not such a good idea on a 64-bit system, where memory
8421083Sanish  * addresses are twice as large as configuration space addresses.  At some
8431083Sanish  * point in the call tree we need to take a stand and say "you are 32-bit
8441083Sanish  * from this time forth", and this seems like a nice self-contained place.
8451083Sanish  */
8461083Sanish 
8471083Sanish uint8_t
8481083Sanish pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr)
8491083Sanish {
8501083Sanish 	pci_acc_cfblk_t *cfp;
8511083Sanish 	uint8_t	rval;
8521083Sanish 	int reg;
8531083Sanish 
8541083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
8551083Sanish 
8561083Sanish 	reg = (int)(uintptr_t)addr;
8571083Sanish 
8581083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
8591083Sanish 
8601083Sanish 	rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
8611083Sanish 	    reg);
8621083Sanish 
8631083Sanish 	return (rval);
8641083Sanish }
8651083Sanish 
8661083Sanish void
8671083Sanish pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
8681083Sanish 	uint8_t *dev_addr, size_t repcount, uint_t flags)
8691083Sanish {
8701083Sanish 	uint8_t *h, *d;
8711083Sanish 
8721083Sanish 	h = host_addr;
8731083Sanish 	d = dev_addr;
8741083Sanish 
8751083Sanish 	if (flags == DDI_DEV_AUTOINCR)
8761083Sanish 		for (; repcount; repcount--)
8771083Sanish 			*h++ = pci_config_rd8(hdlp, d++);
8781083Sanish 	else
8791083Sanish 		for (; repcount; repcount--)
8801083Sanish 			*h++ = pci_config_rd8(hdlp, d);
8811083Sanish }
8821083Sanish 
8831083Sanish uint16_t
8841083Sanish pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr)
8851083Sanish {
8861083Sanish 	pci_acc_cfblk_t *cfp;
8871083Sanish 	uint16_t rval;
8881083Sanish 	int reg;
8891083Sanish 
8901083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
8911083Sanish 
8921083Sanish 	reg = (int)(uintptr_t)addr;
8931083Sanish 
8941083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
8951083Sanish 
8961083Sanish 	rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
8971083Sanish 	    reg);
8981083Sanish 
8991083Sanish 	return (rval);
9001083Sanish }
9011083Sanish 
9021083Sanish void
9031083Sanish pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
9041083Sanish 	uint16_t *dev_addr, size_t repcount, uint_t flags)
9051083Sanish {
9061083Sanish 	uint16_t *h, *d;
9071083Sanish 
9081083Sanish 	h = host_addr;
9091083Sanish 	d = dev_addr;
9101083Sanish 
9111083Sanish 	if (flags == DDI_DEV_AUTOINCR)
9121083Sanish 		for (; repcount; repcount--)
9131083Sanish 			*h++ = pci_config_rd16(hdlp, d++);
9141083Sanish 	else
9151083Sanish 		for (; repcount; repcount--)
9161083Sanish 			*h++ = pci_config_rd16(hdlp, d);
9171083Sanish }
9181083Sanish 
9191083Sanish uint32_t
9201083Sanish pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr)
9211083Sanish {
9221083Sanish 	pci_acc_cfblk_t *cfp;
9231083Sanish 	uint32_t rval;
9241083Sanish 	int reg;
9251083Sanish 
9261083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
9271083Sanish 
9281083Sanish 	reg = (int)(uintptr_t)addr;
9291083Sanish 
9301083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
9311083Sanish 
9321083Sanish 	rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum,
9331083Sanish 	    cfp->c_funcnum, reg);
9341083Sanish 
9351083Sanish 	return (rval);
9361083Sanish }
9371083Sanish 
9381083Sanish void
9391083Sanish pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
9401083Sanish 	uint32_t *dev_addr, size_t repcount, uint_t flags)
9411083Sanish {
9421083Sanish 	uint32_t *h, *d;
9431083Sanish 
9441083Sanish 	h = host_addr;
9451083Sanish 	d = dev_addr;
9461083Sanish 
9471083Sanish 	if (flags == DDI_DEV_AUTOINCR)
9481083Sanish 		for (; repcount; repcount--)
9491083Sanish 			*h++ = pci_config_rd32(hdlp, d++);
9501083Sanish 	else
9511083Sanish 		for (; repcount; repcount--)
9521083Sanish 			*h++ = pci_config_rd32(hdlp, d);
9531083Sanish }
9541083Sanish 
9551083Sanish 
9561083Sanish void
9571083Sanish pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
9581083Sanish {
9591083Sanish 	pci_acc_cfblk_t *cfp;
9601083Sanish 	int reg;
9611083Sanish 
9621083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
9631083Sanish 
9641083Sanish 	reg = (int)(uintptr_t)addr;
9651083Sanish 
9661083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
9671083Sanish 
9681083Sanish 	(*pci_putb_func)(cfp->c_busnum, cfp->c_devnum,
9691083Sanish 	    cfp->c_funcnum, reg, value);
9701083Sanish }
9711083Sanish 
9721083Sanish void
9731083Sanish pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
9741083Sanish 	uint8_t *dev_addr, size_t repcount, uint_t flags)
9751083Sanish {
9761083Sanish 	uint8_t *h, *d;
9771083Sanish 
9781083Sanish 	h = host_addr;
9791083Sanish 	d = dev_addr;
9801083Sanish 
9811083Sanish 	if (flags == DDI_DEV_AUTOINCR)
9821083Sanish 		for (; repcount; repcount--)
9831083Sanish 			pci_config_wr8(hdlp, d++, *h++);
9841083Sanish 	else
9851083Sanish 		for (; repcount; repcount--)
9861083Sanish 			pci_config_wr8(hdlp, d, *h++);
9871083Sanish }
9881083Sanish 
9891083Sanish void
9901083Sanish pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
9911083Sanish {
9921083Sanish 	pci_acc_cfblk_t *cfp;
9931083Sanish 	int reg;
9941083Sanish 
9951083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
9961083Sanish 
9971083Sanish 	reg = (int)(uintptr_t)addr;
9981083Sanish 
9991083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
10001083Sanish 
10011083Sanish 	(*pci_putw_func)(cfp->c_busnum, cfp->c_devnum,
10021083Sanish 	    cfp->c_funcnum, reg, value);
10031083Sanish }
10041083Sanish 
10051083Sanish void
10061083Sanish pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
10071083Sanish 	uint16_t *dev_addr, size_t repcount, uint_t flags)
10081083Sanish {
10091083Sanish 	uint16_t *h, *d;
10101083Sanish 
10111083Sanish 	h = host_addr;
10121083Sanish 	d = dev_addr;
10131083Sanish 
10141083Sanish 	if (flags == DDI_DEV_AUTOINCR)
10151083Sanish 		for (; repcount; repcount--)
10161083Sanish 			pci_config_wr16(hdlp, d++, *h++);
10171083Sanish 	else
10181083Sanish 		for (; repcount; repcount--)
10191083Sanish 			pci_config_wr16(hdlp, d, *h++);
10201083Sanish }
10211083Sanish 
10221083Sanish void
10231083Sanish pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value)
10241083Sanish {
10251083Sanish 	pci_acc_cfblk_t *cfp;
10261083Sanish 	int reg;
10271083Sanish 
10281083Sanish 	ASSERT64(((uintptr_t)addr >> 32) == 0);
10291083Sanish 
10301083Sanish 	reg = (int)(uintptr_t)addr;
10311083Sanish 
10321083Sanish 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
10331083Sanish 
10341083Sanish 	(*pci_putl_func)(cfp->c_busnum, cfp->c_devnum,
10351083Sanish 	    cfp->c_funcnum, reg, value);
10361083Sanish }
10371083Sanish 
10381083Sanish void
10391083Sanish pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
10401083Sanish 	uint32_t *dev_addr, size_t repcount, uint_t flags)
10411083Sanish {
10421083Sanish 	uint32_t *h, *d;
10431083Sanish 
10441083Sanish 	h = host_addr;
10451083Sanish 	d = dev_addr;
10461083Sanish 
10471083Sanish 	if (flags == DDI_DEV_AUTOINCR)
10481083Sanish 		for (; repcount; repcount--)
10491083Sanish 			pci_config_wr32(hdlp, d++, *h++);
10501083Sanish 	else
10511083Sanish 		for (; repcount; repcount--)
10521083Sanish 			pci_config_wr32(hdlp, d, *h++);
10531083Sanish }
10541083Sanish 
10551083Sanish uint64_t
10561083Sanish pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr)
10571083Sanish {
10581083Sanish 	uint32_t lw_val;
10591083Sanish 	uint32_t hi_val;
10601083Sanish 	uint32_t *dp;
10611083Sanish 	uint64_t val;
10621083Sanish 
10631083Sanish 	dp = (uint32_t *)addr;
10641083Sanish 	lw_val = pci_config_rd32(hdlp, dp);
10651083Sanish 	dp++;
10661083Sanish 	hi_val = pci_config_rd32(hdlp, dp);
10671083Sanish 	val = ((uint64_t)hi_val << 32) | lw_val;
10681083Sanish 	return (val);
10691083Sanish }
10701083Sanish 
10711083Sanish void
10721083Sanish pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value)
10731083Sanish {
10741083Sanish 	uint32_t lw_val;
10751083Sanish 	uint32_t hi_val;
10761083Sanish 	uint32_t *dp;
10771083Sanish 
10781083Sanish 	dp = (uint32_t *)addr;
10791083Sanish 	lw_val = (uint32_t)(value & 0xffffffff);
10801083Sanish 	hi_val = (uint32_t)(value >> 32);
10811083Sanish 	pci_config_wr32(hdlp, dp, lw_val);
10821083Sanish 	dp++;
10831083Sanish 	pci_config_wr32(hdlp, dp, hi_val);
10841083Sanish }
10851083Sanish 
10861083Sanish void
10871083Sanish pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
10881083Sanish 	uint64_t *dev_addr, size_t repcount, uint_t flags)
10891083Sanish {
10901083Sanish 	if (flags == DDI_DEV_AUTOINCR) {
10911083Sanish 		for (; repcount; repcount--)
10921083Sanish 			*host_addr++ = pci_config_rd64(hdlp, dev_addr++);
10931083Sanish 	} else {
10941083Sanish 		for (; repcount; repcount--)
10951083Sanish 			*host_addr++ = pci_config_rd64(hdlp, dev_addr);
10961083Sanish 	}
10971083Sanish }
10981083Sanish 
10991083Sanish void
11001083Sanish pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
11011083Sanish 	uint64_t *dev_addr, size_t repcount, uint_t flags)
11021083Sanish {
11031083Sanish 	if (flags == DDI_DEV_AUTOINCR) {
11041083Sanish 		for (; repcount; repcount--)
11051083Sanish 			pci_config_wr64(hdlp, host_addr++, *dev_addr++);
11061083Sanish 	} else {
11071083Sanish 		for (; repcount; repcount--)
11081083Sanish 			pci_config_wr64(hdlp, host_addr++, *dev_addr);
11091083Sanish 	}
11101083Sanish }
11111083Sanish 
11121083Sanish 
11131083Sanish /*
11141083Sanish  * Enable Legacy PCI config space access for the following four north bridges
11151083Sanish  *	Host bridge: AMD HyperTransport Technology Configuration
11161083Sanish  *	Host bridge: AMD Address Map
11171083Sanish  *	Host bridge: AMD DRAM Controller
11181083Sanish  *	Host bridge: AMD Miscellaneous Control
11191083Sanish  */
11201083Sanish int
11211083Sanish is_amd_northbridge(dev_info_t *dip)
11221083Sanish {
11231083Sanish 	int vendor_id, device_id;
11241083Sanish 
11251083Sanish 	vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11261083Sanish 			"vendor-id", -1);
11271083Sanish 	device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11281083Sanish 			"device-id", -1);
11291083Sanish 
11301083Sanish 	if (IS_AMD_NTBRIDGE(vendor_id, device_id))
11311083Sanish 		return (0);
11321083Sanish 
11331083Sanish 	return (1);
11341083Sanish }
1135