xref: /onnv-gate/usr/src/uts/sun4/io/px/px_tools.c (revision 10053:79ff8cfc9153)
1624Sschwartz /*
2624Sschwartz  * CDDL HEADER START
3624Sschwartz  *
4624Sschwartz  * The contents of this file are subject to the terms of the
51772Sjl139090  * Common Development and Distribution License (the "License").
61772Sjl139090  * You may not use this file except in compliance with the License.
7624Sschwartz  *
8624Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9624Sschwartz  * or http://www.opensolaris.org/os/licensing.
10624Sschwartz  * See the License for the specific language governing permissions
11624Sschwartz  * and limitations under the License.
12624Sschwartz  *
13624Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
14624Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15624Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
16624Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
17624Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
18624Sschwartz  *
19624Sschwartz  * CDDL HEADER END
20624Sschwartz  */
21624Sschwartz /*
22*10053SEvan.Yan@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23624Sschwartz  * Use is subject to license terms.
24624Sschwartz  */
25624Sschwartz 
26624Sschwartz #include <sys/types.h>
27624Sschwartz #include <sys/stat.h>
28624Sschwartz #include <sys/cpuvar.h>
29624Sschwartz #include <sys/kmem.h>
30624Sschwartz #include <sys/sunddi.h>
31624Sschwartz #include <sys/hotplug/pci/pcihp.h>
32624Sschwartz #include "px_obj.h"
33624Sschwartz #include <sys/pci_tools.h>
34777Sschwartz #include "px_tools_ext.h"
35624Sschwartz #include "px_tools_var.h"
36624Sschwartz 
37624Sschwartz /*
38624Sschwartz  * PCI Space definitions.
39624Sschwartz  */
40624Sschwartz #define	PCI_CONFIG_SPACE	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
41624Sschwartz #define	PCI_IO_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_IO))
42624Sschwartz #define	PCI_MEM32_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
43624Sschwartz #define	PCI_MEM64_SPACE		(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
44624Sschwartz 
45624Sschwartz /*
46624Sschwartz  * Config space range for a device.  IEEE 1275 spec defines for PCI.
47624Sschwartz  * Space for PCI-express is multiplied by PX_PCI_BDF_OFFSET_DELTA
48624Sschwartz  */
49624Sschwartz #define	DEV_CFG_SPACE_SIZE	\
50624Sschwartz 	(1 << (PCI_REG_FUNC_SHIFT + PX_PCI_BDF_OFFSET_DELTA))
51624Sschwartz 
52624Sschwartz /*
53624Sschwartz  * Offsets of BARS in config space.  First entry of 0 means config space.
54624Sschwartz  * Entries here correlate to pcitool_bars_t enumerated type.
55624Sschwartz  */
56624Sschwartz uint8_t pci_bars[] = {
57624Sschwartz 	0x0,
58624Sschwartz 	PCI_CONF_BASE0,
59624Sschwartz 	PCI_CONF_BASE1,
60624Sschwartz 	PCI_CONF_BASE2,
61624Sschwartz 	PCI_CONF_BASE3,
62624Sschwartz 	PCI_CONF_BASE4,
63624Sschwartz 	PCI_CONF_BASE5,
64624Sschwartz 	PCI_CONF_ROM
65624Sschwartz };
66624Sschwartz 
67624Sschwartz int	pci_num_bars = sizeof (pci_bars) / sizeof (pci_bars[0]);
68624Sschwartz 
69624Sschwartz 
704397Sschwartz /*ARGSUSED*/
71624Sschwartz static int
724397Sschwartz pxtool_intr_info(dev_info_t *dip, void *arg, int mode)
73624Sschwartz {
74*10053SEvan.Yan@Sun.COM 	px_t *px_p = DIP_TO_STATE(dip);
75*10053SEvan.Yan@Sun.COM 	px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
764397Sschwartz 	pcitool_intr_info_t intr_info;
774397Sschwartz 	int rval = SUCCESS;
784397Sschwartz 
794397Sschwartz 	/* If we need user_version, and to ret same user version as passed in */
804397Sschwartz 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
814397Sschwartz 	    DDI_SUCCESS) {
82624Sschwartz 		return (EFAULT);
834397Sschwartz 	}
844397Sschwartz 
854397Sschwartz 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
864397Sschwartz 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
87*10053SEvan.Yan@Sun.COM 	if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
88*10053SEvan.Yan@Sun.COM 		intr_info.num_intr = msi_state_p->msi_cnt;
89*10053SEvan.Yan@Sun.COM 	else
90*10053SEvan.Yan@Sun.COM 		intr_info.num_intr = pxtool_num_inos;
914397Sschwartz 
924397Sschwartz 	intr_info.drvr_version = PCITOOL_VERSION;
934397Sschwartz 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
944397Sschwartz 	    DDI_SUCCESS) {
954397Sschwartz 		rval = EFAULT;
964397Sschwartz 	}
974397Sschwartz 
984397Sschwartz 	return (rval);
99624Sschwartz }
1004397Sschwartz 
1014397Sschwartz 
102624Sschwartz /*
103624Sschwartz  * Get interrupt information for a given ino.
104624Sschwartz  * Returns info only for inos mapped to devices.
105624Sschwartz  *
106624Sschwartz  * Returned info is valid only when iget.num_devs is returned > 0.
107624Sschwartz  * If ino is not enabled or is not mapped to a device,
108624Sschwartz  * iget.num_devs will be returned as = 0.
109624Sschwartz  */
110624Sschwartz /*ARGSUSED*/
111624Sschwartz static int
112624Sschwartz pxtool_get_intr(dev_info_t *dip, void *arg, int mode)
113624Sschwartz {
114624Sschwartz 	/* Array part isn't used here, but oh well... */
115624Sschwartz 	pcitool_intr_get_t partial_iget;
116*10053SEvan.Yan@Sun.COM 	pcitool_intr_get_t *iget = &partial_iget;
117624Sschwartz 	int copyout_rval;
118624Sschwartz 	sysino_t sysino;
119624Sschwartz 	intr_valid_state_t intr_valid_state;
120624Sschwartz 	cpuid_t old_cpu_id;
121624Sschwartz 	px_t *px_p = DIP_TO_STATE(dip);
122624Sschwartz 	size_t	iget_kmem_alloc_size = 0;
123*10053SEvan.Yan@Sun.COM 	int rval = EIO;
124624Sschwartz 
125624Sschwartz 	/* Read in just the header part, no array section. */
126624Sschwartz 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
127624Sschwartz 	    DDI_SUCCESS)
128624Sschwartz 		return (EFAULT);
129624Sschwartz 
130*10053SEvan.Yan@Sun.COM 	iget->status = PCITOOL_IO_ERROR;
131*10053SEvan.Yan@Sun.COM 
132*10053SEvan.Yan@Sun.COM 	if (iget->flags & PCITOOL_INTR_FLAG_GET_MSI) {
133*10053SEvan.Yan@Sun.COM 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
134*10053SEvan.Yan@Sun.COM 		pci_msi_valid_state_t	msi_state;
135*10053SEvan.Yan@Sun.COM 		msiqid_t	msiq_id;
136624Sschwartz 
137*10053SEvan.Yan@Sun.COM 		if ((iget->msi < msi_state_p->msi_1st_msinum) ||
138*10053SEvan.Yan@Sun.COM 		    (iget->msi >= (msi_state_p->msi_1st_msinum +
139*10053SEvan.Yan@Sun.COM 		    msi_state_p->msi_cnt))) {
140*10053SEvan.Yan@Sun.COM 			iget->status = PCITOOL_INVALID_MSI;
141*10053SEvan.Yan@Sun.COM 			rval = EINVAL;
142*10053SEvan.Yan@Sun.COM 			goto done_get_intr;
143*10053SEvan.Yan@Sun.COM 		}
144*10053SEvan.Yan@Sun.COM 
145*10053SEvan.Yan@Sun.COM 		if ((px_lib_msi_getvalid(dip, iget->msi,
146*10053SEvan.Yan@Sun.COM 		    &msi_state) != DDI_SUCCESS) ||
147*10053SEvan.Yan@Sun.COM 		    (msi_state != PCI_MSI_VALID))
148*10053SEvan.Yan@Sun.COM 			goto done_get_intr;
149*10053SEvan.Yan@Sun.COM 
150*10053SEvan.Yan@Sun.COM 		if (px_lib_msi_getmsiq(dip, iget->msi,
151*10053SEvan.Yan@Sun.COM 		    &msiq_id) != DDI_SUCCESS)
152*10053SEvan.Yan@Sun.COM 			goto done_get_intr;
153*10053SEvan.Yan@Sun.COM 
154*10053SEvan.Yan@Sun.COM 		iget->ino = px_msiqid_to_devino(px_p, msiq_id);
155*10053SEvan.Yan@Sun.COM 	} else {
156*10053SEvan.Yan@Sun.COM 		iget->msi = (uint32_t)-1;
157*10053SEvan.Yan@Sun.COM 	}
158624Sschwartz 
159624Sschwartz 	/* Validate argument. */
160*10053SEvan.Yan@Sun.COM 	if (iget->ino > pxtool_num_inos) {
161*10053SEvan.Yan@Sun.COM 		iget->status = PCITOOL_INVALID_INO;
162*10053SEvan.Yan@Sun.COM 		rval = EINVAL;
163624Sschwartz 		goto done_get_intr;
164624Sschwartz 	}
165624Sschwartz 
166624Sschwartz 	/* Caller wants device information returned. */
167*10053SEvan.Yan@Sun.COM 	if (iget->num_devs_ret > 0) {
168624Sschwartz 		/*
169624Sschwartz 		 * Allocate room.
170624Sschwartz 		 * Note if num_devs == 0 iget remains pointing to
171624Sschwartz 		 * partial_iget.
172624Sschwartz 		 */
173*10053SEvan.Yan@Sun.COM 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget->num_devs_ret);
174*10053SEvan.Yan@Sun.COM 		iget = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
175624Sschwartz 
176624Sschwartz 		/* Read in whole structure to verify there's room. */
177624Sschwartz 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
178624Sschwartz 		    SUCCESS) {
179624Sschwartz 
180624Sschwartz 			/* Be consistent and just return EFAULT here. */
181624Sschwartz 			kmem_free(iget, iget_kmem_alloc_size);
182624Sschwartz 
183624Sschwartz 			return (EFAULT);
184624Sschwartz 		}
185624Sschwartz 	}
186624Sschwartz 
187624Sschwartz 	/* Convert leaf-wide intr to system-wide intr */
188*10053SEvan.Yan@Sun.COM 	if (px_lib_intr_devino_to_sysino(dip, iget->ino, &sysino) !=
189*10053SEvan.Yan@Sun.COM 	    DDI_SUCCESS) {
190624Sschwartz 		iget->status = PCITOOL_IO_ERROR;
191624Sschwartz 		rval = EIO;
192624Sschwartz 		goto done_get_intr;
193624Sschwartz 	}
194624Sschwartz 
195624Sschwartz 	/* Operate only on inos which are already enabled. */
196*10053SEvan.Yan@Sun.COM 	if (px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
197*10053SEvan.Yan@Sun.COM 	    DDI_SUCCESS) {
198624Sschwartz 		iget->status = PCITOOL_IO_ERROR;
199624Sschwartz 		rval = EIO;
200624Sschwartz 		goto done_get_intr;
201624Sschwartz 	}
202624Sschwartz 
203624Sschwartz 	/*
204624Sschwartz 	 * Consider all valid inos: those mapped to the root complex itself
205624Sschwartz 	 * as well as those mapped to devices.
206624Sschwartz 	 */
207624Sschwartz 	if (intr_valid_state == INTR_VALID) {
208624Sschwartz 		/*
2092973Sgovinda 		 * The following looks up the px_ino and returns
210624Sschwartz 		 * info of devices mapped to this ino.
211624Sschwartz 		 */
212*10053SEvan.Yan@Sun.COM 		iget->num_devs = pxtool_ib_get_ino_devs(px_p, iget->ino,
213*10053SEvan.Yan@Sun.COM 		    iget->msi, &iget->num_devs_ret, iget->dev);
214624Sschwartz 
215*10053SEvan.Yan@Sun.COM 		if (px_ib_get_intr_target(px_p, iget->ino,
216*10053SEvan.Yan@Sun.COM 		    &old_cpu_id) != DDI_SUCCESS) {
217624Sschwartz 			iget->status = PCITOOL_IO_ERROR;
218624Sschwartz 			rval = EIO;
219624Sschwartz 			goto done_get_intr;
220624Sschwartz 		}
221*10053SEvan.Yan@Sun.COM 
222624Sschwartz 		iget->cpu_id = old_cpu_id;
223624Sschwartz 	}
224624Sschwartz 
225624Sschwartz 	iget->status = PCITOOL_SUCCESS;
226624Sschwartz 	rval = SUCCESS;
227624Sschwartz 
228624Sschwartz done_get_intr:
2294397Sschwartz 	iget->drvr_version = PCITOOL_VERSION;
230624Sschwartz 	copyout_rval =
231*10053SEvan.Yan@Sun.COM 	    ddi_copyout(iget, arg, PCITOOL_IGET_SIZE(iget->num_devs_ret), mode);
232624Sschwartz 
233624Sschwartz 	if (iget_kmem_alloc_size > 0)
234624Sschwartz 		kmem_free(iget, iget_kmem_alloc_size);
235624Sschwartz 
236624Sschwartz 	if (copyout_rval != DDI_SUCCESS)
237624Sschwartz 		rval = EFAULT;
238624Sschwartz 
239624Sschwartz 	return (rval);
240624Sschwartz }
241624Sschwartz 
242624Sschwartz 
243624Sschwartz /*
244624Sschwartz  * Associate a new CPU with a given ino.
245624Sschwartz  *
246624Sschwartz  * Operate only on inos which are already mapped to devices.
247624Sschwartz  */
248624Sschwartz static int
249624Sschwartz pxtool_set_intr(dev_info_t *dip, void *arg, int mode)
250624Sschwartz {
251624Sschwartz 	pcitool_intr_set_t iset;
252624Sschwartz 	cpuid_t old_cpu_id;
253624Sschwartz 	sysino_t sysino;
254*10053SEvan.Yan@Sun.COM 	intr_valid_state_t intr_valid_state;
255624Sschwartz 	px_t *px_p = DIP_TO_STATE(dip);
256*10053SEvan.Yan@Sun.COM 	msiqid_t msiq_id;
257*10053SEvan.Yan@Sun.COM 	int rval = EIO;
258*10053SEvan.Yan@Sun.COM 	int ret = DDI_SUCCESS;
2594397Sschwartz 	size_t copyinout_size;
260624Sschwartz 
2614397Sschwartz 	bzero(&iset, sizeof (pcitool_intr_set_t));
2624397Sschwartz 
2634397Sschwartz 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
2644397Sschwartz 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
2654397Sschwartz 
2664397Sschwartz 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
267624Sschwartz 		return (EFAULT);
268624Sschwartz 
2694397Sschwartz 	switch (iset.user_version) {
2704397Sschwartz 	case PCITOOL_V1:
2714397Sschwartz 		break;
2724397Sschwartz 
2734397Sschwartz 	case PCITOOL_V2:
2744397Sschwartz 		copyinout_size = sizeof (pcitool_intr_set_t);
2754397Sschwartz 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
2764397Sschwartz 			return (EFAULT);
2774397Sschwartz 		break;
2784397Sschwartz 
2794397Sschwartz 	default:
2804397Sschwartz 		iset.status = PCITOOL_OUT_OF_RANGE;
2814397Sschwartz 		rval = ENOTSUP;
2824397Sschwartz 		goto done_set_intr;
2834397Sschwartz 	}
2844397Sschwartz 
285*10053SEvan.Yan@Sun.COM 	if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
2864397Sschwartz 		iset.status = PCITOOL_IO_ERROR;
2874397Sschwartz 		rval = ENOTSUP;
2884397Sschwartz 		goto done_set_intr;
2894397Sschwartz 	}
2904397Sschwartz 
291*10053SEvan.Yan@Sun.COM 	iset.status = PCITOOL_IO_ERROR;
292*10053SEvan.Yan@Sun.COM 
293*10053SEvan.Yan@Sun.COM 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
294*10053SEvan.Yan@Sun.COM 		px_msi_state_t	*msi_state_p = &px_p->px_ib_p->ib_msi_state;
295*10053SEvan.Yan@Sun.COM 		pci_msi_valid_state_t	msi_state;
296*10053SEvan.Yan@Sun.COM 
297*10053SEvan.Yan@Sun.COM 		if ((iset.msi < msi_state_p->msi_1st_msinum) ||
298*10053SEvan.Yan@Sun.COM 		    (iset.msi >= (msi_state_p->msi_1st_msinum +
299*10053SEvan.Yan@Sun.COM 		    msi_state_p->msi_cnt))) {
300*10053SEvan.Yan@Sun.COM 			iset.status = PCITOOL_INVALID_MSI;
301*10053SEvan.Yan@Sun.COM 			rval = EINVAL;
302*10053SEvan.Yan@Sun.COM 			goto done_set_intr;
303*10053SEvan.Yan@Sun.COM 		}
304*10053SEvan.Yan@Sun.COM 
305*10053SEvan.Yan@Sun.COM 		if ((px_lib_msi_getvalid(dip, iset.msi,
306*10053SEvan.Yan@Sun.COM 		    &msi_state) != DDI_SUCCESS) ||
307*10053SEvan.Yan@Sun.COM 		    (msi_state != PCI_MSI_VALID))
308*10053SEvan.Yan@Sun.COM 			goto done_set_intr;
309*10053SEvan.Yan@Sun.COM 
310*10053SEvan.Yan@Sun.COM 		if (px_lib_msi_getmsiq(dip, iset.msi,
311*10053SEvan.Yan@Sun.COM 		    &msiq_id) != DDI_SUCCESS)
312*10053SEvan.Yan@Sun.COM 			goto done_set_intr;
313*10053SEvan.Yan@Sun.COM 
314*10053SEvan.Yan@Sun.COM 		iset.ino = px_msiqid_to_devino(px_p, msiq_id);
315*10053SEvan.Yan@Sun.COM 	} else {
316*10053SEvan.Yan@Sun.COM 		iset.msi = (uint32_t)-1;
317*10053SEvan.Yan@Sun.COM 	}
318624Sschwartz 
319624Sschwartz 	/* Validate input argument. */
320*10053SEvan.Yan@Sun.COM 	if (iset.ino > pxtool_num_inos) {
321*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_INVALID_INO;
322*10053SEvan.Yan@Sun.COM 		rval = EINVAL;
323*10053SEvan.Yan@Sun.COM 		goto done_set_intr;
324*10053SEvan.Yan@Sun.COM 	}
325*10053SEvan.Yan@Sun.COM 
326*10053SEvan.Yan@Sun.COM 	/* Convert leaf-wide intr to system-wide intr */
327*10053SEvan.Yan@Sun.COM 	if (px_lib_intr_devino_to_sysino(dip, iset.ino, &sysino) !=
328*10053SEvan.Yan@Sun.COM 	    DDI_SUCCESS)
329624Sschwartz 		goto done_set_intr;
330624Sschwartz 
331*10053SEvan.Yan@Sun.COM 	/* Operate only on inos which are already enabled. */
332*10053SEvan.Yan@Sun.COM 	if ((px_lib_intr_getvalid(dip, sysino, &intr_valid_state) !=
333*10053SEvan.Yan@Sun.COM 	    DDI_SUCCESS) || (intr_valid_state == INTR_NOTVALID))
334624Sschwartz 		goto done_set_intr;
335624Sschwartz 
336624Sschwartz 	/*
337*10053SEvan.Yan@Sun.COM 	 * Consider all valid inos: those mapped to the root complex itself
338*10053SEvan.Yan@Sun.COM 	 * as well as those mapped to devices.
339624Sschwartz 	 */
340*10053SEvan.Yan@Sun.COM 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS)
341*10053SEvan.Yan@Sun.COM 		goto done_set_intr;
342624Sschwartz 
343*10053SEvan.Yan@Sun.COM 	if (iset.flags & PCITOOL_INTR_FLAG_SET_MSI) {
344*10053SEvan.Yan@Sun.COM 		ddi_intr_handle_impl_t	hdle;
345624Sschwartz 
346*10053SEvan.Yan@Sun.COM 		bzero(&hdle, sizeof (ddi_intr_handle_impl_t));
347*10053SEvan.Yan@Sun.COM 		if (pxtool_ib_get_msi_info(px_p, iset.ino, iset.msi,
348*10053SEvan.Yan@Sun.COM 		    &hdle) != DDI_SUCCESS) {
349*10053SEvan.Yan@Sun.COM 			iset.status = PCITOOL_INVALID_MSI;
350*10053SEvan.Yan@Sun.COM 			rval = EINVAL;
351624Sschwartz 			goto done_set_intr;
352*10053SEvan.Yan@Sun.COM 		}
353624Sschwartz 
354*10053SEvan.Yan@Sun.COM 		if ((ret = px_ib_set_msix_target(px_p, &hdle, iset.msi,
355*10053SEvan.Yan@Sun.COM 		    iset.cpu_id)) == DDI_SUCCESS) {
356*10053SEvan.Yan@Sun.COM 			(void) px_lib_msi_getmsiq(dip, iset.msi, &msiq_id);
357*10053SEvan.Yan@Sun.COM 			iset.ino = px_msiqid_to_devino(px_p, msiq_id);
358*10053SEvan.Yan@Sun.COM 			iset.cpu_id = old_cpu_id;
359*10053SEvan.Yan@Sun.COM 			iset.status = PCITOOL_SUCCESS;
360*10053SEvan.Yan@Sun.COM 			rval = SUCCESS;
361*10053SEvan.Yan@Sun.COM 			goto done_set_intr;
362*10053SEvan.Yan@Sun.COM 		}
363*10053SEvan.Yan@Sun.COM 	} else {
364*10053SEvan.Yan@Sun.COM 		if ((ret = px_ib_set_intr_target(px_p, iset.ino,
365*10053SEvan.Yan@Sun.COM 		    iset.cpu_id)) == DDI_SUCCESS) {
366*10053SEvan.Yan@Sun.COM 			iset.cpu_id = old_cpu_id;
367*10053SEvan.Yan@Sun.COM 			iset.status = PCITOOL_SUCCESS;
368*10053SEvan.Yan@Sun.COM 			rval = SUCCESS;
369*10053SEvan.Yan@Sun.COM 			goto done_set_intr;
370*10053SEvan.Yan@Sun.COM 		}
371*10053SEvan.Yan@Sun.COM 	}
372624Sschwartz 
373*10053SEvan.Yan@Sun.COM 	switch (ret) {
374*10053SEvan.Yan@Sun.COM 	case DDI_EPENDING:
375*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_PENDING_INTRTIMEOUT;
376*10053SEvan.Yan@Sun.COM 		rval = ETIME;
377*10053SEvan.Yan@Sun.COM 		break;
378*10053SEvan.Yan@Sun.COM 	case DDI_EINVAL:
379624Sschwartz 		iset.status = PCITOOL_INVALID_CPUID;
380624Sschwartz 		rval = EINVAL;
381*10053SEvan.Yan@Sun.COM 		break;
382*10053SEvan.Yan@Sun.COM 	default:
383*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_IO_ERROR;
384*10053SEvan.Yan@Sun.COM 		rval = EIO;
385*10053SEvan.Yan@Sun.COM 		break;
386624Sschwartz 	}
387624Sschwartz 
388624Sschwartz done_set_intr:
3894397Sschwartz 	iset.drvr_version = PCITOOL_VERSION;
3904397Sschwartz 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
391624Sschwartz 		rval = EFAULT;
392624Sschwartz 
393624Sschwartz 	return (rval);
394624Sschwartz }
395624Sschwartz 
396624Sschwartz 
397624Sschwartz /* Main function for handling interrupt CPU binding requests and queries. */
398624Sschwartz int
399624Sschwartz pxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
400624Sschwartz {
401624Sschwartz 	int rval = SUCCESS;
402624Sschwartz 
403624Sschwartz 	switch (cmd) {
404624Sschwartz 
4054397Sschwartz 	/* Get system interrupt information. */
4064397Sschwartz 	case PCITOOL_SYSTEM_INTR_INFO:
4074397Sschwartz 		rval = pxtool_intr_info(dip, arg, mode);
408624Sschwartz 		break;
409624Sschwartz 
410624Sschwartz 	/* Get interrupt information for a given ino. */
411624Sschwartz 	case PCITOOL_DEVICE_GET_INTR:
412624Sschwartz 		rval = pxtool_get_intr(dip, arg, mode);
413624Sschwartz 		break;
414624Sschwartz 
415624Sschwartz 	/* Associate a new CPU with a given ino. */
416624Sschwartz 	case PCITOOL_DEVICE_SET_INTR:
417624Sschwartz 		rval = pxtool_set_intr(dip, arg, mode);
418624Sschwartz 		break;
419624Sschwartz 
420624Sschwartz 	default:
421624Sschwartz 		rval = ENOTTY;
422624Sschwartz 	}
423624Sschwartz 
424624Sschwartz 	return (rval);
425624Sschwartz }
426624Sschwartz 
427624Sschwartz 
428624Sschwartz static int
429624Sschwartz pxtool_validate_barnum_bdf(pcitool_reg_t *prg)
430624Sschwartz {
431624Sschwartz 	int rval = SUCCESS;
432624Sschwartz 
433624Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
434624Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
435624Sschwartz 		rval = EINVAL;
436624Sschwartz 
437624Sschwartz 	/* Validate address arguments of bus / dev / func */
438624Sschwartz 	} else if (((prg->bus_no &
439624Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
440624Sschwartz 	    ((prg->dev_no &
441624Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
442624Sschwartz 	    ((prg->func_no &
443624Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
444624Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
445624Sschwartz 		rval = EINVAL;
446624Sschwartz 	}
447624Sschwartz 
448624Sschwartz 	return (rval);
449624Sschwartz }
450624Sschwartz 
4511064Sschwartz /*
4521064Sschwartz  * px_p defines which leaf, space defines which space in that leaf, offset
4531064Sschwartz  * defines the offset within the specified space.
4541064Sschwartz  *
4551064Sschwartz  * This returns the physical address of the corresponding location.
4561064Sschwartz  */
4571064Sschwartz static uintptr_t
4581064Sschwartz pxtool_get_phys_addr(px_t *px_p, int space, uint64_t offset)
4591064Sschwartz {
4601064Sschwartz 	uint64_t range_base;
4611064Sschwartz 	int rval;
4622053Sschwartz 	pci_regspec_t dev_regspec;
4632053Sschwartz 	struct regspec xlated_regspec;
4641064Sschwartz 	dev_info_t *dip = px_p->px_dip;
4651064Sschwartz 
4661064Sschwartz 	/*
4671064Sschwartz 	 * Assume that requested entity is small enough to be on the same page.
4681064Sschwartz 	 * PCItool checks alignment so that this will be true for single
4691064Sschwartz 	 * accesses.
4701064Sschwartz 	 */
4712053Sschwartz 	dev_regspec.pci_phys_hi = space << PCI_REG_ADDR_SHIFT;
4722053Sschwartz 	if (space == PCI_CONFIG_SPACE) {
4732053Sschwartz 		dev_regspec.pci_phys_hi +=
4742053Sschwartz 		    (offset & (PCI_REG_BDFR_M ^ PCI_REG_REG_M));
4752053Sschwartz 		dev_regspec.pci_phys_low = offset & PCI_REG_REG_M;
4763625Segillett 		dev_regspec.pci_phys_mid = 0;	/* Not used */
4772053Sschwartz 	} else {
4782053Sschwartz 		dev_regspec.pci_phys_mid = offset >> 32;
4792053Sschwartz 		dev_regspec.pci_phys_low = offset & 0xffffffff;
4802053Sschwartz 	}
4812053Sschwartz 	dev_regspec.pci_size_hi = 0;	/* Not used. */
4822053Sschwartz 
4832053Sschwartz 	/* Note: object is guaranteed to be within a page. */
4842053Sschwartz 	dev_regspec.pci_size_low = 4;
4852053Sschwartz 
4862053Sschwartz 	rval = px_xlate_reg(px_p, &dev_regspec, &xlated_regspec);
4872053Sschwartz 
4881064Sschwartz 	DBG(DBG_TOOLS, dip,
4892053Sschwartz 	    "space:0x%d, offset:0x%" PRIx64 "\n", space, offset);
4901064Sschwartz 
4911064Sschwartz 	if (rval != DDI_SUCCESS)
4921064Sschwartz 		return (NULL);
4932053Sschwartz 
4942053Sschwartz 	/* Bustype here returns the high order address bits. */
4952053Sschwartz 	xlated_regspec.regspec_bustype &= px_get_rng_parent_hi_mask(px_p);
4962053Sschwartz 
4972053Sschwartz 	range_base = (((uint64_t)xlated_regspec.regspec_bustype) << 32) +
4982053Sschwartz 	    xlated_regspec.regspec_addr;
4992053Sschwartz 	DBG(DBG_TOOLS, dip,
5002053Sschwartz 	    "regspec: hi:0x%x, lo:0x%x, sz:0x%x, range base:0x%" PRIx64 "\n",
5012053Sschwartz 	    xlated_regspec.regspec_bustype, xlated_regspec.regspec_addr,
5022053Sschwartz 	    xlated_regspec.regspec_size, range_base);
5032053Sschwartz 
5042053Sschwartz 	return ((uintptr_t)range_base);
5051064Sschwartz }
5061064Sschwartz 
5071064Sschwartz 
508624Sschwartz static int
5091064Sschwartz pxtool_get_bar(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *bar_p,
5101064Sschwartz     uint32_t *space_p)
511624Sschwartz {
512624Sschwartz 	int rval;
5131064Sschwartz 	uint64_t off_in_space;
514624Sschwartz 	pcitool_reg_t cfg_prg = *prg_p;	/* Make local copy. */
515624Sschwartz 	dev_info_t *dip = px_p->px_dip;
516624Sschwartz 
517624Sschwartz 	*space_p = PCI_MEM32_SPACE;
518624Sschwartz 	*bar_p = 0;
519624Sschwartz 
520624Sschwartz 	/*
521624Sschwartz 	 * Translate BAR number into offset of the BAR in
522624Sschwartz 	 * the device's config space.
523624Sschwartz 	 */
524624Sschwartz 	cfg_prg.acc_attr =
525624Sschwartz 	    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
526624Sschwartz 
5271064Sschwartz 	/*
5281064Sschwartz 	 * Note: sun4u acc function uses phys_addr which includes offset.
5291064Sschwartz 	 * sun4v acc function doesn't use phys_addr but uses cfg_prg.offset.
5301064Sschwartz 	 */
5311064Sschwartz 	cfg_prg.offset = PCI_BAR_OFFSET((*prg_p));
5322053Sschwartz 	off_in_space = PX_GET_BDF(prg_p) + cfg_prg.offset;
5331064Sschwartz 	cfg_prg.phys_addr = pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE,
5341064Sschwartz 	    off_in_space);
5351064Sschwartz 
5361064Sschwartz 	DBG(DBG_TOOLS, dip,
5371064Sschwartz 	    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", barnum:%d\n",
5381064Sschwartz 	    off_in_space, cfg_prg.phys_addr, cfg_prg.barnum);
539624Sschwartz 
540624Sschwartz 	/*
541624Sschwartz 	 * Get Bus Address Register (BAR) from config space.
542624Sschwartz 	 * cfg_prg.offset is the offset into config space of the
543624Sschwartz 	 * BAR desired.  prg_p->status is modified on error.
544624Sschwartz 	 */
5451064Sschwartz 	rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
546624Sschwartz 
547624Sschwartz 	if (rval != SUCCESS) {
548624Sschwartz 		prg_p->status = cfg_prg.status;
549624Sschwartz 		return (rval);
550624Sschwartz 	}
551624Sschwartz 
552624Sschwartz 	DBG(DBG_TOOLS, dip, "bar returned is 0x%" PRIx64 "\n", *bar_p);
553624Sschwartz 
554624Sschwartz 	/*
555624Sschwartz 	 * BAR has bits saying this space is IO space, unless
556624Sschwartz 	 * this is the ROM address register.
557624Sschwartz 	 */
558624Sschwartz 	if (((PCI_BASE_SPACE_M & *bar_p) == PCI_BASE_SPACE_IO) &&
559624Sschwartz 	    (cfg_prg.offset != PCI_CONF_ROM)) {
560624Sschwartz 		*space_p = PCI_IO_SPACE;
561624Sschwartz 		*bar_p &= PCI_BASE_IO_ADDR_M;
562624Sschwartz 
563624Sschwartz 	/*
564624Sschwartz 	 * BAR has bits saying this space is 64 bit memory
565624Sschwartz 	 * space, unless this is the ROM address register.
566624Sschwartz 	 *
567624Sschwartz 	 * The 64 bit address stored in two BAR cells is not
568624Sschwartz 	 * necessarily aligned on an 8-byte boundary.
569624Sschwartz 	 * Need to keep the first 4 bytes read,
570624Sschwartz 	 * and do a separate read of the high 4 bytes.
571624Sschwartz 	 */
572624Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar_p) &&
573624Sschwartz 	    (cfg_prg.offset != PCI_CONF_ROM)) {
574624Sschwartz 
575624Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar_p & ~PCI_BASE_TYPE_ALL);
576624Sschwartz 
577624Sschwartz 		/* Don't try to read the next 4 bytes past the end of BARs. */
578624Sschwartz 		if (cfg_prg.offset >= PCI_CONF_BASE5) {
579624Sschwartz 			prg_p->status = PCITOOL_OUT_OF_RANGE;
580624Sschwartz 			return (EIO);
581624Sschwartz 		}
582624Sschwartz 
583624Sschwartz 		/* Access device.  prg_p->status is modified on error. */
584624Sschwartz 		cfg_prg.phys_addr += sizeof (uint32_t);
585624Sschwartz 		cfg_prg.offset += sizeof (uint32_t);
586624Sschwartz 
5871064Sschwartz 		rval = pxtool_pcicfg_access(px_p, &cfg_prg, bar_p, PX_ISREAD);
588624Sschwartz 		if (rval != SUCCESS) {
589624Sschwartz 			prg_p->status = cfg_prg.status;
590624Sschwartz 			return (rval);
591624Sschwartz 		}
592624Sschwartz 
593624Sschwartz 		/*
594624Sschwartz 		 * Honor the 64 bit BAR as such, only when the upper 32 bits
595624Sschwartz 		 * store a non-zero value.
596624Sschwartz 		 */
597624Sschwartz 		if (*bar_p) {
598624Sschwartz 			*space_p = PCI_MEM64_SPACE;
599624Sschwartz 			*bar_p = (*bar_p << 32) | low_bytes;
600624Sschwartz 		} else
601624Sschwartz 			*bar_p = low_bytes;
602624Sschwartz 
603624Sschwartz 	} else if (cfg_prg.offset == PCI_CONF_ROM) { /* ROM requested */
604624Sschwartz 
605624Sschwartz 		/*
606624Sschwartz 		 * ROM enabled. Filter ROM enable bit from the BAR.
607624Sschwartz 		 * Treat as Mem32 henceforth.
608624Sschwartz 		 */
609624Sschwartz 		if (!(*bar_p & PCI_BASE_ROM_ENABLE))
610624Sschwartz 			*bar_p ^= PCI_BASE_ROM_ENABLE;
611624Sschwartz 
612624Sschwartz 		else {	/* ROM disabled. */
613624Sschwartz 			prg_p->status = PCITOOL_ROM_DISABLED;
614624Sschwartz 			return (EIO);
615624Sschwartz 		}
616624Sschwartz 	}
617624Sschwartz 
618624Sschwartz 	/* Accept a bar of 0 only for IO space. */
619624Sschwartz 	if ((*space_p != PCI_IO_SPACE) && (!(*bar_p))) {
620624Sschwartz 		prg_p->status = PCITOOL_INVALID_ADDRESS;
621624Sschwartz 		return (EINVAL);
622624Sschwartz 	}
623624Sschwartz 
624624Sschwartz 	return (SUCCESS);
625624Sschwartz }
626624Sschwartz 
627624Sschwartz 
628624Sschwartz /* Perform register accesses on PCI leaf devices. */
629624Sschwartz int
630624Sschwartz pxtool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
631624Sschwartz {
632624Sschwartz 	pcitool_reg_t	prg;
633624Sschwartz 	uint64_t	bar;
634624Sschwartz 	uint32_t	space;
6351064Sschwartz 	uint64_t	off_in_space;
636624Sschwartz 	boolean_t	write_flag = B_FALSE;
637624Sschwartz 	px_t		*px_p = DIP_TO_STATE(dip);
638624Sschwartz 	int		rval = 0;
639624Sschwartz 
640624Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
641624Sschwartz 		write_flag = B_TRUE;
642624Sschwartz 
643624Sschwartz 	DBG(DBG_TOOLS, dip, "pxtool_dev_reg_ops set/get reg\n");
644624Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t),
645624Sschwartz 	    mode) != DDI_SUCCESS) {
646624Sschwartz 		DBG(DBG_TOOLS, dip, "Error reading arguments\n");
647624Sschwartz 		return (EFAULT);
648624Sschwartz 	}
649624Sschwartz 
650624Sschwartz 	if ((rval = pxtool_dev_reg_ops_platchk(dip, &prg)) != SUCCESS) {
651624Sschwartz 		goto done_reg;
652624Sschwartz 	}
653624Sschwartz 
654624Sschwartz 	DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
655624Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
656624Sschwartz 	DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n",
657624Sschwartz 	    prg.barnum, prg.offset, prg.acc_attr);
658624Sschwartz 
659624Sschwartz 	if ((rval = pxtool_validate_barnum_bdf(&prg)) != SUCCESS)
660624Sschwartz 		goto done_reg;
661624Sschwartz 
662624Sschwartz 	if (prg.barnum == 0) {	/* Proper config space desired. */
663624Sschwartz 
6641064Sschwartz 		/* Enforce offset limits. */
6651064Sschwartz 		if (prg.offset >= DEV_CFG_SPACE_SIZE) {
6661064Sschwartz 			DBG(DBG_TOOLS, dip,
6671064Sschwartz 			    "Config space offset 0x%" PRIx64 " out of range\n",
6681064Sschwartz 			    prg.offset);
6691064Sschwartz 			prg.status = PCITOOL_OUT_OF_RANGE;
6701064Sschwartz 			rval = EINVAL;
6711064Sschwartz 			goto done_reg;
6721064Sschwartz 		}
6731064Sschwartz 
6741064Sschwartz 		/*
6751064Sschwartz 		 * For sun4v, config space base won't be known.
6761064Sschwartz 		 * pxtool_get_phys_addr will return zero.
6771064Sschwartz 		 * Note that for sun4v, phys_addr isn't
6781064Sschwartz 		 * used for making config space accesses.
6791064Sschwartz 		 *
6801064Sschwartz 		 * For sun4u, assume that phys_addr will come back valid.
6811064Sschwartz 		 */
6822053Sschwartz 		/*
6832053Sschwartz 		 * Accessed entity is assumed small enough to be on one page.
6842053Sschwartz 		 *
6852053Sschwartz 		 * Since config space is less than a page and is aligned to
6862053Sschwartz 		 * 0x1000, a device's entire config space will be on a single
6872053Sschwartz 		 * page.  Pass the device's base config space address here,
6882053Sschwartz 		 * then add the offset within that space later.  This works
6892053Sschwartz 		 * around an issue in px_xlate_reg (called by
6902053Sschwartz 		 * pxtool_get_phys_addr) which accepts only a 256 byte
6912053Sschwartz 		 * range within a device.
6922053Sschwartz 		 */
6932053Sschwartz 		off_in_space = PX_GET_BDF(&prg);
6942053Sschwartz 		prg.phys_addr =
6952053Sschwartz 		    pxtool_get_phys_addr(px_p, PCI_CONFIG_SPACE, off_in_space);
6962053Sschwartz 		prg.phys_addr += prg.offset;
697624Sschwartz 
698624Sschwartz 		DBG(DBG_TOOLS, dip,
6991064Sschwartz 		    "off_in_space:0x%" PRIx64 ", phys_addr:0x%" PRIx64 ", "
7001064Sschwartz 		    "end:%s\n", off_in_space, prg.phys_addr,
701624Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr) ? "big":"ltl");
702624Sschwartz 
703624Sschwartz 		/*
704624Sschwartz 		 * Access device.  pr.status is modified.
705624Sschwartz 		 * BDF is assumed valid at this point.
706624Sschwartz 		 */
7071064Sschwartz 		rval = pxtool_pcicfg_access(px_p, &prg, &prg.data, write_flag);
708624Sschwartz 		goto done_reg;
709624Sschwartz 	}
710624Sschwartz 
711624Sschwartz 	/* IO/ MEM/ MEM64 space. */
712624Sschwartz 
7131064Sschwartz 	if ((rval = pxtool_get_bar(px_p, &prg, &bar, &space)) != SUCCESS)
714624Sschwartz 		goto done_reg;
715624Sschwartz 
7161064Sschwartz 	switch (space) {
7171064Sschwartz 	case PCI_MEM32_SPACE:
718624Sschwartz 
719624Sschwartz 		DBG(DBG_TOOLS, dip, "32 bit mem space\n");
720624Sschwartz 
721624Sschwartz 		/* Can't write to ROM */
722624Sschwartz 		if ((PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) && (write_flag)) {
723624Sschwartz 			prg.status = PCITOOL_ROM_WRITE;
724624Sschwartz 			rval = EIO;
725624Sschwartz 			goto done_reg;
726624Sschwartz 		}
7271064Sschwartz 		break;
728624Sschwartz 
7291064Sschwartz 	case PCI_IO_SPACE:
7301064Sschwartz 		DBG(DBG_TOOLS, dip, "IO space\n");
7311064Sschwartz 		break;
7321064Sschwartz 
7331064Sschwartz 	case PCI_MEM64_SPACE:
7341064Sschwartz 		DBG(DBG_TOOLS, dip,
7351064Sschwartz 		    "64 bit mem space.  64-bit bar is 0x%" PRIx64 "\n", bar);
7361064Sschwartz 		break;
7371064Sschwartz 
7381064Sschwartz 	default:
7391064Sschwartz 		DBG(DBG_TOOLS, dip, "Unknown space!\n");
7401064Sschwartz 		prg.status = PCITOOL_IO_ERROR;
7411064Sschwartz 		rval = EIO;
7421064Sschwartz 		goto done_reg;
743624Sschwartz 	}
744624Sschwartz 
745624Sschwartz 	/*
7461064Sschwartz 	 * Common code for all IO/MEM range spaces.
7471064Sschwartz 	 *
748624Sschwartz 	 * Use offset provided by caller to index into desired space.
749624Sschwartz 	 * Note that prg.status is modified on error.
750624Sschwartz 	 */
7511064Sschwartz 	off_in_space = bar + prg.offset;
7521064Sschwartz 	prg.phys_addr = pxtool_get_phys_addr(px_p, space, off_in_space);
753624Sschwartz 
7541064Sschwartz 	DBG(DBG_TOOLS, dip,
7551064Sschwartz 	    "addr in bar:0x%" PRIx64 ", offset:0x%" PRIx64 ", "
7561064Sschwartz 	    "phys_addr:0x%" PRIx64 "\n", bar, prg.offset, prg.phys_addr);
7571064Sschwartz 
7581064Sschwartz 	rval = pxtool_pciiomem_access(px_p, &prg, &prg.data, write_flag);
759624Sschwartz 
760624Sschwartz done_reg:
7614397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
762624Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
763624Sschwartz 	    mode) != DDI_SUCCESS) {
764624Sschwartz 		DBG(DBG_TOOLS, dip, "Error returning arguments.\n");
765624Sschwartz 		rval = EFAULT;
766624Sschwartz 	}
767624Sschwartz 	return (rval);
768624Sschwartz }
769624Sschwartz 
770624Sschwartz 
771624Sschwartz int
772624Sschwartz pxtool_init(dev_info_t *dip)
773624Sschwartz {
774624Sschwartz 	int instance = ddi_get_instance(dip);
775624Sschwartz 
776624Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
777624Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
778624Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS) {
779624Sschwartz 		return (DDI_FAILURE);
780624Sschwartz 	}
781624Sschwartz 
782624Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
783624Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
784624Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
785624Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
786624Sschwartz 		return (DDI_FAILURE);
787624Sschwartz 	}
788624Sschwartz 
789624Sschwartz 	return (DDI_SUCCESS);
790624Sschwartz }
791624Sschwartz 
792624Sschwartz 
793624Sschwartz void
794624Sschwartz pxtool_uninit(dev_info_t *dip)
795624Sschwartz {
796624Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
797624Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
798624Sschwartz }
799