xref: /onnv-gate/usr/src/uts/i86pc/io/pci/pci.c (revision 354:c0082238b026)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  *	Host to PCI local bus driver
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/conf.h>
340Sstevel@tonic-gate #include <sys/kmem.h>
350Sstevel@tonic-gate #include <sys/debug.h>
360Sstevel@tonic-gate #include <sys/modctl.h>
370Sstevel@tonic-gate #include <sys/autoconf.h>
380Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
390Sstevel@tonic-gate #include <sys/ddi_subrdefs.h>
400Sstevel@tonic-gate #include <sys/pci.h>
410Sstevel@tonic-gate #include <sys/pci_impl.h>
420Sstevel@tonic-gate #include <sys/ddi.h>
430Sstevel@tonic-gate #include <sys/sunddi.h>
440Sstevel@tonic-gate #include <sys/sunndi.h>
450Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
460Sstevel@tonic-gate #include <sys/pci_cfgspace.h>
470Sstevel@tonic-gate #include <sys/avintr.h>
480Sstevel@tonic-gate #include <sys/psm.h>
490Sstevel@tonic-gate #include <sys/pci_intr_lib.h>
500Sstevel@tonic-gate #include <sys/policy.h>
510Sstevel@tonic-gate #include <sys/pci_tools.h>
520Sstevel@tonic-gate #include <sys/pci_tools_var.h>
530Sstevel@tonic-gate #include "pci_var.h"
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /* Save minimal state. */
560Sstevel@tonic-gate void *pci_statep;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  * Bus Operation functions
600Sstevel@tonic-gate  */
610Sstevel@tonic-gate static int	pci_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
620Sstevel@tonic-gate 		    off_t, off_t, caddr_t *);
630Sstevel@tonic-gate static int	pci_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
640Sstevel@tonic-gate 		    void *, void *);
650Sstevel@tonic-gate static int	pci_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
660Sstevel@tonic-gate 		    ddi_intr_handle_impl_t *, void *);
670Sstevel@tonic-gate static int	pci_get_priority(dev_info_t *, int, int *);
680Sstevel@tonic-gate static int	pci_get_nintrs(dev_info_t *, int, int *);
690Sstevel@tonic-gate static int	pci_enable_intr(dev_info_t *, dev_info_t *,
700Sstevel@tonic-gate 		    ddi_intr_handle_impl_t *, uint32_t);
710Sstevel@tonic-gate static void	pci_disable_intr(dev_info_t *, dev_info_t *,
720Sstevel@tonic-gate 		    ddi_intr_handle_impl_t *, uint32_t);
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /* Extern decalrations */
750Sstevel@tonic-gate extern int	(*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
760Sstevel@tonic-gate 		    psm_intr_op_t, int *);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate struct bus_ops pci_bus_ops = {
790Sstevel@tonic-gate 	BUSO_REV,
800Sstevel@tonic-gate 	pci_bus_map,
810Sstevel@tonic-gate 	NULL,
820Sstevel@tonic-gate 	NULL,
830Sstevel@tonic-gate 	NULL,
840Sstevel@tonic-gate 	i_ddi_map_fault,
850Sstevel@tonic-gate 	ddi_dma_map,
860Sstevel@tonic-gate 	ddi_dma_allochdl,
870Sstevel@tonic-gate 	ddi_dma_freehdl,
880Sstevel@tonic-gate 	ddi_dma_bindhdl,
890Sstevel@tonic-gate 	ddi_dma_unbindhdl,
900Sstevel@tonic-gate 	ddi_dma_flush,
910Sstevel@tonic-gate 	ddi_dma_win,
920Sstevel@tonic-gate 	ddi_dma_mctl,
930Sstevel@tonic-gate 	pci_ctlops,
940Sstevel@tonic-gate 	ddi_bus_prop_op,
950Sstevel@tonic-gate 	0,		/* (*bus_get_eventcookie)();	*/
960Sstevel@tonic-gate 	0,		/* (*bus_add_eventcall)();	*/
970Sstevel@tonic-gate 	0,		/* (*bus_remove_eventcall)();	*/
980Sstevel@tonic-gate 	0,		/* (*bus_post_event)();		*/
990Sstevel@tonic-gate 	0,		/* (*bus_intr_ctl)(); */
1000Sstevel@tonic-gate 	0,		/* (*bus_config)(); */
1010Sstevel@tonic-gate 	0,		/* (*bus_unconfig)(); */
1020Sstevel@tonic-gate 	NULL,		/* (*bus_fm_init)(); */
1030Sstevel@tonic-gate 	NULL,		/* (*bus_fm_fini)(); */
1040Sstevel@tonic-gate 	NULL,		/* (*bus_fm_access_enter)(); */
1050Sstevel@tonic-gate 	NULL,		/* (*bus_fm_access_exit)(); */
1060Sstevel@tonic-gate 	NULL,		/* (*bus_power)(); */
1070Sstevel@tonic-gate 	pci_intr_ops	/* (*bus_intr_op)(); */
1080Sstevel@tonic-gate };
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static int pci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
1110Sstevel@tonic-gate static int pci_close(dev_t dev, int flags, int otyp, cred_t *credp);
1120Sstevel@tonic-gate static int pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1130Sstevel@tonic-gate     int *rvalp);
1140Sstevel@tonic-gate static int pci_prop_op(dev_t dev, dev_info_t *devi, ddi_prop_op_t prop_op,
1150Sstevel@tonic-gate     int flags, char *name, caddr_t valuep, int *lengthp);
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate  * One goal here is to leverage off of the pcihp.c source without making
1190Sstevel@tonic-gate  * changes to it.  Call into it's cb_ops directly if needed, piggybacking
1200Sstevel@tonic-gate  * anything else needed by the pci_tools.c module.  Only pci_tools and pcihp
121117Sschwartz  * will be opening PCI nexus driver file descriptors.
1220Sstevel@tonic-gate  */
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate struct cb_ops pci_cb_ops = {
1250Sstevel@tonic-gate 	pci_open,			/* open */
1260Sstevel@tonic-gate 	pci_close,			/* close */
1270Sstevel@tonic-gate 	nodev,				/* strategy */
1280Sstevel@tonic-gate 	nodev,				/* print */
1290Sstevel@tonic-gate 	nodev,				/* dump */
1300Sstevel@tonic-gate 	nodev,				/* read */
1310Sstevel@tonic-gate 	nodev,				/* write */
1320Sstevel@tonic-gate 	pci_ioctl,			/* ioctl */
1330Sstevel@tonic-gate 	nodev,				/* devmap */
1340Sstevel@tonic-gate 	nodev,				/* mmap */
1350Sstevel@tonic-gate 	nodev,				/* segmap */
1360Sstevel@tonic-gate 	nochpoll,			/* poll */
1370Sstevel@tonic-gate 	pci_prop_op,			/* cb_prop_op */
1380Sstevel@tonic-gate 	NULL,				/* streamtab */
1390Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1400Sstevel@tonic-gate 	CB_REV,				/* rev */
1410Sstevel@tonic-gate 	nodev,				/* int (*cb_aread)() */
1420Sstevel@tonic-gate 	nodev				/* int (*cb_awrite)() */
1430Sstevel@tonic-gate };
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate /*
1460Sstevel@tonic-gate  * Device Node Operation functions
1470Sstevel@tonic-gate  */
1480Sstevel@tonic-gate static int pci_info(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg,
1490Sstevel@tonic-gate     void **result);
1500Sstevel@tonic-gate static int pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
1510Sstevel@tonic-gate static int pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate struct dev_ops pci_ops = {
1540Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1550Sstevel@tonic-gate 	0,			/* refcnt  */
1560Sstevel@tonic-gate 	pci_info,		/* info */
1570Sstevel@tonic-gate 	nulldev,		/* identify */
1580Sstevel@tonic-gate 	nulldev,		/* probe */
1590Sstevel@tonic-gate 	pci_attach,		/* attach */
1600Sstevel@tonic-gate 	pci_detach,		/* detach */
1610Sstevel@tonic-gate 	nulldev,		/* reset */
1620Sstevel@tonic-gate 	&pci_cb_ops,		/* driver operations */
1630Sstevel@tonic-gate 	&pci_bus_ops		/* bus operations */
1640Sstevel@tonic-gate };
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /*
167*354Smyers  * This variable controls the default setting of the command register
168*354Smyers  * for pci devices.  See pci_initchild() for details.
169*354Smyers  */
170*354Smyers static ushort_t pci_command_default = PCI_COMM_ME |
171*354Smyers 					PCI_COMM_MAE |
172*354Smyers 					PCI_COMM_IO;
173*354Smyers 
174*354Smyers /*
1750Sstevel@tonic-gate  * Internal routines in support of particular pci_ctlops.
1760Sstevel@tonic-gate  */
1770Sstevel@tonic-gate static int pci_removechild(dev_info_t *child);
1780Sstevel@tonic-gate static int pci_initchild(dev_info_t *child);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate /*
181117Sschwartz  * Miscellaneous internal functions
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate static int pci_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate  * These are the access routines.  The pci_bus_map sets the handle
1870Sstevel@tonic-gate  * to point to these.
1880Sstevel@tonic-gate  */
1890Sstevel@tonic-gate static uint8_t pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr);
1900Sstevel@tonic-gate static uint16_t pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr);
1910Sstevel@tonic-gate static uint32_t pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr);
1920Sstevel@tonic-gate static uint64_t pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate static void pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr,
1950Sstevel@tonic-gate 				uint8_t value);
1960Sstevel@tonic-gate static void pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr,
1970Sstevel@tonic-gate 				uint16_t value);
1980Sstevel@tonic-gate static void pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr,
1990Sstevel@tonic-gate 				uint32_t value);
2000Sstevel@tonic-gate static void pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr,
2010Sstevel@tonic-gate 				uint64_t value);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate static void pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
2040Sstevel@tonic-gate 	uint8_t *dev_addr, size_t repcount, uint_t flags);
2050Sstevel@tonic-gate static void pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
2060Sstevel@tonic-gate 	uint16_t *dev_addr, size_t repcount, uint_t flags);
2070Sstevel@tonic-gate static void pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
2080Sstevel@tonic-gate 	uint32_t *dev_addr, size_t repcount, uint_t flags);
2090Sstevel@tonic-gate static void pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
2100Sstevel@tonic-gate 	uint64_t *dev_addr, size_t repcount, uint_t flags);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate static void pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
2130Sstevel@tonic-gate 	uint8_t *dev_addr, size_t repcount, uint_t flags);
2140Sstevel@tonic-gate static void pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
2150Sstevel@tonic-gate 	uint16_t *dev_addr, size_t repcount, uint_t flags);
2160Sstevel@tonic-gate static void pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
2170Sstevel@tonic-gate 	uint32_t *dev_addr, size_t repcount, uint_t flags);
2180Sstevel@tonic-gate static void pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
2190Sstevel@tonic-gate 	uint64_t *dev_addr, size_t repcount, uint_t flags);
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate /*
2220Sstevel@tonic-gate  * Module linkage information for the kernel.
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate static struct modldrv modldrv = {
2260Sstevel@tonic-gate 	&mod_driverops, /* Type of module */
2270Sstevel@tonic-gate 	"host to PCI nexus driver %I%",
2280Sstevel@tonic-gate 	&pci_ops,	/* driver ops */
2290Sstevel@tonic-gate };
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate static struct modlinkage modlinkage = {
2320Sstevel@tonic-gate 	MODREV_1,
2330Sstevel@tonic-gate 	(void *)&modldrv,
2340Sstevel@tonic-gate 	NULL
2350Sstevel@tonic-gate };
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate int
2380Sstevel@tonic-gate _init(void)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	int e;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/*
2430Sstevel@tonic-gate 	 * Initialize per-pci bus soft state pointer.
2440Sstevel@tonic-gate 	 */
2450Sstevel@tonic-gate 	e = ddi_soft_state_init(&pci_statep, sizeof (pci_state_t), 1);
2460Sstevel@tonic-gate 	if (e != 0)
2470Sstevel@tonic-gate 		return (e);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) != 0)
2500Sstevel@tonic-gate 		ddi_soft_state_fini(&pci_statep);
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	return (e);
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate int
2560Sstevel@tonic-gate _fini(void)
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	int rc;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	rc = mod_remove(&modlinkage);
2610Sstevel@tonic-gate 	if (rc != 0)
2620Sstevel@tonic-gate 		return (rc);
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	ddi_soft_state_fini(&pci_statep);
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	return (rc);
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate int
2700Sstevel@tonic-gate _info(struct modinfo *modinfop)
2710Sstevel@tonic-gate {
2720Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate /*ARGSUSED*/
2760Sstevel@tonic-gate static int
2770Sstevel@tonic-gate pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2780Sstevel@tonic-gate {
2790Sstevel@tonic-gate 	/*
2800Sstevel@tonic-gate 	 * Use the minor number as constructed by pcihp, as the index value to
2810Sstevel@tonic-gate 	 * ddi_soft_state_zalloc.
2820Sstevel@tonic-gate 	 */
283117Sschwartz 	int instance = ddi_get_instance(devi);
2840Sstevel@tonic-gate 	pci_state_t *pcip = NULL;
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type", "pci")
2870Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
2880Sstevel@tonic-gate 		cmn_err(CE_WARN, "pci:  'device_type' prop create failed");
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
291117Sschwartz 	if (ddi_soft_state_zalloc(pci_statep, instance) == DDI_SUCCESS) {
292117Sschwartz 		pcip = ddi_get_soft_state(pci_statep, instance);
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	if (pcip == NULL) {
296117Sschwartz 		goto bad_soft_state;
2970Sstevel@tonic-gate 	}
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	pcip->pci_dip = devi;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	/*
3020Sstevel@tonic-gate 	 * Initialize hotplug support on this bus. At minimum
3030Sstevel@tonic-gate 	 * (for non hotplug bus) this would create ":devctl" minor
3040Sstevel@tonic-gate 	 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
3050Sstevel@tonic-gate 	 * to this bus.
3060Sstevel@tonic-gate 	 */
3070Sstevel@tonic-gate 	if (pcihp_init(devi) != DDI_SUCCESS) {
3080Sstevel@tonic-gate 		cmn_err(CE_WARN, "pci: Failed to setup hotplug framework");
309117Sschwartz 		goto bad_pcihp_init;
310117Sschwartz 	}
311117Sschwartz 
312117Sschwartz 	if (pcitool_init(devi) != DDI_SUCCESS) {
313117Sschwartz 		goto bad_pcitool_init;
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	ddi_report_dev(devi);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	return (DDI_SUCCESS);
319117Sschwartz 
320117Sschwartz bad_pcitool_init:
321117Sschwartz 	(void) pcihp_uninit(devi);
322117Sschwartz bad_pcihp_init:
323117Sschwartz 	ddi_soft_state_free(pci_statep, instance);
324117Sschwartz bad_soft_state:
325117Sschwartz 	return (DDI_FAILURE);
3260Sstevel@tonic-gate }
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate /*ARGSUSED*/
3290Sstevel@tonic-gate static int
3300Sstevel@tonic-gate pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3310Sstevel@tonic-gate {
332117Sschwartz 	/* Uninitialize pcitool support. */
333117Sschwartz 	pcitool_uninit(devi);
334117Sschwartz 
335117Sschwartz 	/* Uninitialize hotplug support on this bus. */
3360Sstevel@tonic-gate 	(void) pcihp_uninit(devi);
337117Sschwartz 
3380Sstevel@tonic-gate 	ddi_soft_state_free(pci_statep, DIP_TO_MINOR(devi));
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	return (DDI_SUCCESS);
3410Sstevel@tonic-gate }
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate static int
3440Sstevel@tonic-gate pci_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
3450Sstevel@tonic-gate {
3460Sstevel@tonic-gate 	pci_regspec_t *assigned_addr;
3470Sstevel@tonic-gate 	int	assigned_addr_len;
3480Sstevel@tonic-gate 	uint_t 	phys_hi;
3490Sstevel@tonic-gate 	int	i;
3500Sstevel@tonic-gate 	int 	rc;
3510Sstevel@tonic-gate 	int 	number;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	phys_hi = pci_rp->pci_phys_hi;
3540Sstevel@tonic-gate 	if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) ||
3550Sstevel@tonic-gate 		(phys_hi & PCI_RELOCAT_B))
3560Sstevel@tonic-gate 		return (DDI_SUCCESS);
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	/*
3590Sstevel@tonic-gate 	 * the "reg" property specifies relocatable, get and interpret the
3600Sstevel@tonic-gate 	 * "assigned-addresses" property.
3610Sstevel@tonic-gate 	 */
3620Sstevel@tonic-gate 	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
3630Sstevel@tonic-gate 		DDI_PROP_DONTPASS, "assigned-addresses",
3640Sstevel@tonic-gate 		(int **)&assigned_addr, (uint_t *)&assigned_addr_len);
3650Sstevel@tonic-gate 	if (rc != DDI_PROP_SUCCESS)
3660Sstevel@tonic-gate 		return (DDI_FAILURE);
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	/*
3690Sstevel@tonic-gate 	 * Scan the "assigned-addresses" for one that matches the specified
3700Sstevel@tonic-gate 	 * "reg" property entry.
3710Sstevel@tonic-gate 	 */
3720Sstevel@tonic-gate 	phys_hi &= PCI_CONF_ADDR_MASK;
3730Sstevel@tonic-gate 	number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int));
3740Sstevel@tonic-gate 	for (i = 0; i < number; i++) {
3750Sstevel@tonic-gate 		if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
3760Sstevel@tonic-gate 				phys_hi) {
3770Sstevel@tonic-gate 			pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
3780Sstevel@tonic-gate 			pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
3790Sstevel@tonic-gate 			ddi_prop_free(assigned_addr);
3800Sstevel@tonic-gate 			return (DDI_SUCCESS);
3810Sstevel@tonic-gate 		}
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	ddi_prop_free(assigned_addr);
3850Sstevel@tonic-gate 	return (DDI_FAILURE);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate static int
3890Sstevel@tonic-gate pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
3900Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *vaddrp)
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate 	struct regspec reg;
3930Sstevel@tonic-gate 	ddi_map_req_t mr;
3940Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
3950Sstevel@tonic-gate 	ddi_acc_impl_t *ap;
3960Sstevel@tonic-gate 	pci_regspec_t pci_reg;
3970Sstevel@tonic-gate 	pci_regspec_t *pci_rp;
3980Sstevel@tonic-gate 	int 	rnumber;
3990Sstevel@tonic-gate 	int	length;
4000Sstevel@tonic-gate 	int	rc;
4010Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
4020Sstevel@tonic-gate 	int	space;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	mr = *mp; /* Get private copy of request */
4060Sstevel@tonic-gate 	mp = &mr;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	/*
4090Sstevel@tonic-gate 	 * check for register number
4100Sstevel@tonic-gate 	 */
4110Sstevel@tonic-gate 	switch (mp->map_type) {
4120Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
4130Sstevel@tonic-gate 		pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
4140Sstevel@tonic-gate 		pci_rp = &pci_reg;
4150Sstevel@tonic-gate 		if (pci_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
4160Sstevel@tonic-gate 			return (DDI_FAILURE);
4170Sstevel@tonic-gate 		break;
4180Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
4190Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
4200Sstevel@tonic-gate 		/*
4210Sstevel@tonic-gate 		 * get ALL "reg" properties for dip, select the one of
4220Sstevel@tonic-gate 		 * of interest. In x86, "assigned-addresses" property
4230Sstevel@tonic-gate 		 * is identical to the "reg" property, so there is no
4240Sstevel@tonic-gate 		 * need to cross check the two to determine the physical
4250Sstevel@tonic-gate 		 * address of the registers.
4260Sstevel@tonic-gate 		 * This routine still performs some validity checks to
4270Sstevel@tonic-gate 		 * make sure that everything is okay.
4280Sstevel@tonic-gate 		 */
4290Sstevel@tonic-gate 		rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
4300Sstevel@tonic-gate 			DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
4310Sstevel@tonic-gate 			(uint_t *)&length);
4320Sstevel@tonic-gate 		if (rc != DDI_PROP_SUCCESS) {
4330Sstevel@tonic-gate 			return (DDI_FAILURE);
4340Sstevel@tonic-gate 		}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 		/*
4370Sstevel@tonic-gate 		 * validate the register number.
4380Sstevel@tonic-gate 		 */
4390Sstevel@tonic-gate 		length /= (sizeof (pci_regspec_t) / sizeof (int));
4400Sstevel@tonic-gate 		if (rnumber >= length) {
4410Sstevel@tonic-gate 			ddi_prop_free(pci_rp);
4420Sstevel@tonic-gate 			return (DDI_FAILURE);
4430Sstevel@tonic-gate 		}
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 		/*
4460Sstevel@tonic-gate 		 * copy the required entry.
4470Sstevel@tonic-gate 		 */
4480Sstevel@tonic-gate 		pci_reg = pci_rp[rnumber];
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 		/*
4510Sstevel@tonic-gate 		 * free the memory allocated by ddi_prop_lookup_int_array
4520Sstevel@tonic-gate 		 */
4530Sstevel@tonic-gate 		ddi_prop_free(pci_rp);
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		pci_rp = &pci_reg;
4560Sstevel@tonic-gate 		if (pci_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
4570Sstevel@tonic-gate 			return (DDI_FAILURE);
4580Sstevel@tonic-gate 		mp->map_type = DDI_MT_REGSPEC;
4590Sstevel@tonic-gate 		break;
4600Sstevel@tonic-gate 	default:
4610Sstevel@tonic-gate 		return (DDI_ME_INVAL);
4620Sstevel@tonic-gate 	}
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	/*
4670Sstevel@tonic-gate 	 * check for unmap and unlock of address space
4680Sstevel@tonic-gate 	 */
4690Sstevel@tonic-gate 	if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) {
4700Sstevel@tonic-gate 		/*
4710Sstevel@tonic-gate 		 * Adjust offset and length
4720Sstevel@tonic-gate 		 * A non-zero length means override the one in the regspec.
4730Sstevel@tonic-gate 		 */
4740Sstevel@tonic-gate 		pci_rp->pci_phys_low += (uint_t)offset;
4750Sstevel@tonic-gate 		if (len != 0)
4760Sstevel@tonic-gate 			pci_rp->pci_size_low = len;
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		switch (space) {
4790Sstevel@tonic-gate 		case PCI_ADDR_CONFIG:
4800Sstevel@tonic-gate 			/* No work required on unmap of Config space */
4810Sstevel@tonic-gate 			return (DDI_SUCCESS);
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		case PCI_ADDR_IO:
4840Sstevel@tonic-gate 			reg.regspec_bustype = 1;
4850Sstevel@tonic-gate 			break;
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 		case PCI_ADDR_MEM64:
4880Sstevel@tonic-gate 			/*
4890Sstevel@tonic-gate 			 * MEM64 requires special treatment on map, to check
4900Sstevel@tonic-gate 			 * that the device is below 4G.  On unmap, however,
4910Sstevel@tonic-gate 			 * we can assume that everything is OK... the map
4920Sstevel@tonic-gate 			 * must have succeeded.
4930Sstevel@tonic-gate 			 */
4940Sstevel@tonic-gate 			/* FALLTHROUGH */
4950Sstevel@tonic-gate 		case PCI_ADDR_MEM32:
4960Sstevel@tonic-gate 			reg.regspec_bustype = 0;
4970Sstevel@tonic-gate 			break;
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 		default:
5000Sstevel@tonic-gate 			return (DDI_FAILURE);
5010Sstevel@tonic-gate 		}
5020Sstevel@tonic-gate 		reg.regspec_addr = pci_rp->pci_phys_low;
5030Sstevel@tonic-gate 		reg.regspec_size = pci_rp->pci_size_low;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 		mp->map_obj.rp = &reg;
5060Sstevel@tonic-gate 		return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	/* check for user mapping request - not legal for Config */
5110Sstevel@tonic-gate 	if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) {
5120Sstevel@tonic-gate 		return (DDI_FAILURE);
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	/*
5160Sstevel@tonic-gate 	 * check for config space
5170Sstevel@tonic-gate 	 * On x86, CONFIG is not mapped via MMU and there is
5180Sstevel@tonic-gate 	 * no endian-ness issues. Set the attr field in the handle to
5190Sstevel@tonic-gate 	 * indicate that the common routines to call the nexus driver.
5200Sstevel@tonic-gate 	 */
5210Sstevel@tonic-gate 	if (space == PCI_ADDR_CONFIG) {
5220Sstevel@tonic-gate 		hp = (ddi_acc_hdl_t *)mp->map_handlep;
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 		if (hp == NULL) {
5250Sstevel@tonic-gate 			/* Can't map config space without a handle */
5260Sstevel@tonic-gate 			return (DDI_FAILURE);
5270Sstevel@tonic-gate 		}
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)hp->ah_platform_private;
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 		/* endian-ness check */
5320Sstevel@tonic-gate 		if (hp->ah_acc.devacc_attr_endian_flags == DDI_STRUCTURE_BE_ACC)
5330Sstevel@tonic-gate 			return (DDI_FAILURE);
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 		/*
5360Sstevel@tonic-gate 		 * range check
5370Sstevel@tonic-gate 		 */
5380Sstevel@tonic-gate 		if ((offset >= 256) || (len > 256) || (offset + len > 256))
5390Sstevel@tonic-gate 			return (DDI_FAILURE);
5400Sstevel@tonic-gate 		*vaddrp = (caddr_t)offset;
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 		ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE;
5430Sstevel@tonic-gate 		ap->ahi_put8 = pci_config_wr8;
5440Sstevel@tonic-gate 		ap->ahi_get8 = pci_config_rd8;
5450Sstevel@tonic-gate 		ap->ahi_put64 = pci_config_wr64;
5460Sstevel@tonic-gate 		ap->ahi_get64 = pci_config_rd64;
5470Sstevel@tonic-gate 		ap->ahi_rep_put8 = pci_config_rep_wr8;
5480Sstevel@tonic-gate 		ap->ahi_rep_get8 = pci_config_rep_rd8;
5490Sstevel@tonic-gate 		ap->ahi_rep_put64 = pci_config_rep_wr64;
5500Sstevel@tonic-gate 		ap->ahi_rep_get64 = pci_config_rep_rd64;
5510Sstevel@tonic-gate 		ap->ahi_get16 = pci_config_rd16;
5520Sstevel@tonic-gate 		ap->ahi_get32 = pci_config_rd32;
5530Sstevel@tonic-gate 		ap->ahi_put16 = pci_config_wr16;
5540Sstevel@tonic-gate 		ap->ahi_put32 = pci_config_wr32;
5550Sstevel@tonic-gate 		ap->ahi_rep_get16 = pci_config_rep_rd16;
5560Sstevel@tonic-gate 		ap->ahi_rep_get32 = pci_config_rep_rd32;
5570Sstevel@tonic-gate 		ap->ahi_rep_put16 = pci_config_rep_wr16;
5580Sstevel@tonic-gate 		ap->ahi_rep_put32 = pci_config_rep_wr32;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		/* Initialize to default check/notify functions */
5610Sstevel@tonic-gate 		ap->ahi_fault_check = i_ddi_acc_fault_check;
5620Sstevel@tonic-gate 		ap->ahi_fault_notify = i_ddi_acc_fault_notify;
5630Sstevel@tonic-gate 		ap->ahi_fault = 0;
5640Sstevel@tonic-gate 		impl_acc_err_init(hp);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 		/* record the device address for future reference */
5670Sstevel@tonic-gate 		cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private;
5680Sstevel@tonic-gate 		cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
5690Sstevel@tonic-gate 		cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
5700Sstevel@tonic-gate 		cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 		return (DDI_SUCCESS);
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/*
5760Sstevel@tonic-gate 	 * range check
5770Sstevel@tonic-gate 	 */
5780Sstevel@tonic-gate 	if ((offset >= pci_rp->pci_size_low) ||
5790Sstevel@tonic-gate 	    (len > pci_rp->pci_size_low) ||
5800Sstevel@tonic-gate 	    (offset + len > pci_rp->pci_size_low)) {
5810Sstevel@tonic-gate 		return (DDI_FAILURE);
5820Sstevel@tonic-gate 	}
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	/*
5850Sstevel@tonic-gate 	 * Adjust offset and length
5860Sstevel@tonic-gate 	 * A non-zero length means override the one in the regspec.
5870Sstevel@tonic-gate 	 */
5880Sstevel@tonic-gate 	pci_rp->pci_phys_low += (uint_t)offset;
5890Sstevel@tonic-gate 	if (len != 0)
5900Sstevel@tonic-gate 		pci_rp->pci_size_low = len;
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	/*
5930Sstevel@tonic-gate 	 * convert the pci regsec into the generic regspec used by the
5940Sstevel@tonic-gate 	 * parent root nexus driver.
5950Sstevel@tonic-gate 	 */
5960Sstevel@tonic-gate 	switch (space) {
5970Sstevel@tonic-gate 	case PCI_ADDR_IO:
5980Sstevel@tonic-gate 		reg.regspec_bustype = 1;
5990Sstevel@tonic-gate 		break;
6000Sstevel@tonic-gate 	case PCI_ADDR_MEM64:
6010Sstevel@tonic-gate 		/*
6020Sstevel@tonic-gate 		 * We can't handle 64-bit devices that are mapped above
6030Sstevel@tonic-gate 		 * 4G or that are larger than 4G.
6040Sstevel@tonic-gate 		 */
6050Sstevel@tonic-gate 		if (pci_rp->pci_phys_mid != 0 ||
6060Sstevel@tonic-gate 		    pci_rp->pci_size_hi != 0)
6070Sstevel@tonic-gate 			return (DDI_FAILURE);
6080Sstevel@tonic-gate 		/*
6090Sstevel@tonic-gate 		 * Other than that, we can treat them as 32-bit mappings
6100Sstevel@tonic-gate 		 */
6110Sstevel@tonic-gate 		/* FALLTHROUGH */
6120Sstevel@tonic-gate 	case PCI_ADDR_MEM32:
6130Sstevel@tonic-gate 		reg.regspec_bustype = 0;
6140Sstevel@tonic-gate 		break;
6150Sstevel@tonic-gate 	default:
6160Sstevel@tonic-gate 		return (DDI_FAILURE);
6170Sstevel@tonic-gate 	}
6180Sstevel@tonic-gate 	reg.regspec_addr = pci_rp->pci_phys_low;
6190Sstevel@tonic-gate 	reg.regspec_size = pci_rp->pci_size_low;
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	mp->map_obj.rp = &reg;
6220Sstevel@tonic-gate 	return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate  * pci_get_priority:
6280Sstevel@tonic-gate  *	Figure out the priority of the device
6290Sstevel@tonic-gate  */
6300Sstevel@tonic-gate static int
6310Sstevel@tonic-gate pci_get_priority(dev_info_t *dip, int inum, int *pri)
6320Sstevel@tonic-gate {
6330Sstevel@tonic-gate 	struct intrspec *ispec;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p\n",
6360Sstevel@tonic-gate 	    (void *)dip));
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, inum)) ==
6390Sstevel@tonic-gate 	    NULL)
6400Sstevel@tonic-gate 		return (DDI_FAILURE);
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	*pri = ispec->intrspec_pri;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	return (DDI_SUCCESS);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate /*
6490Sstevel@tonic-gate  * pci_get_nintrs:
6500Sstevel@tonic-gate  *	Figure out how many interrupts the device supports
6510Sstevel@tonic-gate  */
6520Sstevel@tonic-gate static int
6530Sstevel@tonic-gate pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	int	ret;
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	*nintrs = 0;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if (DDI_INTR_IS_MSI_OR_MSIX(type))
6600Sstevel@tonic-gate 		ret = pci_msi_get_nintrs(dip, type, nintrs);
6610Sstevel@tonic-gate 	else {
6620Sstevel@tonic-gate 		ret = DDI_FAILURE;
6630Sstevel@tonic-gate 		if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6640Sstevel@tonic-gate 		    "interrupts", -1) != -1) {
6650Sstevel@tonic-gate 			*nintrs = 1;
6660Sstevel@tonic-gate 			ret = DDI_SUCCESS;
6670Sstevel@tonic-gate 		}
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	return (ret);
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate /*
6750Sstevel@tonic-gate  * pci_intr_ops: bus_intr_op() function for interrupt support
6760Sstevel@tonic-gate  */
6770Sstevel@tonic-gate /* ARGSUSED */
6780Sstevel@tonic-gate static int
6790Sstevel@tonic-gate pci_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
680117Sschwartz 	ddi_intr_handle_impl_t *hdlp, void *result)
6810Sstevel@tonic-gate {
6820Sstevel@tonic-gate 	int			priority = 0;
6830Sstevel@tonic-gate 	int			psm_status = 0;
6840Sstevel@tonic-gate 	int			pci_status = 0;
6850Sstevel@tonic-gate 	int			pci_rval, psm_rval = PSM_FAILURE;
6860Sstevel@tonic-gate 	int			types = 0;
6870Sstevel@tonic-gate 	int			i, j;
6880Sstevel@tonic-gate 	int			behavior;
6890Sstevel@tonic-gate 	ddi_intrspec_t		isp;
6900Sstevel@tonic-gate 	struct intrspec		*ispec;
6910Sstevel@tonic-gate 	ddi_intr_handle_impl_t	tmp_hdl;
6920Sstevel@tonic-gate 	ddi_intr_msix_t		*msix_p;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT,
6950Sstevel@tonic-gate 	    "pci_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
6960Sstevel@tonic-gate 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	/* Process the request */
6990Sstevel@tonic-gate 	switch (intr_op) {
7000Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
7010Sstevel@tonic-gate 		/* Fixed supported by default */
7020Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 		/* Figure out if MSI or MSI-X is supported? */
7050Sstevel@tonic-gate 		if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
7060Sstevel@tonic-gate 			return (DDI_SUCCESS);
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 		if (psm_intr_ops != NULL) {
7090Sstevel@tonic-gate 			/* MSI or MSI-X is supported, OR it in */
7100Sstevel@tonic-gate 			*(int *)result |= types;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 			tmp_hdl.ih_type = *(int *)result;
7130Sstevel@tonic-gate 			(void) (*psm_intr_ops)(rdip, &tmp_hdl,
7140Sstevel@tonic-gate 			    PSM_INTR_OP_CHECK_MSI, result);
7150Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: rdip: 0x%p "
7160Sstevel@tonic-gate 			    "supported types: 0x%x\n", (void *)rdip,
7170Sstevel@tonic-gate 			    *(int *)result));
7180Sstevel@tonic-gate 		}
7190Sstevel@tonic-gate 		break;
7200Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
7210Sstevel@tonic-gate 		if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
7220Sstevel@tonic-gate 			return (DDI_FAILURE);
7230Sstevel@tonic-gate 		break;
7240Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
7250Sstevel@tonic-gate 		/*
7260Sstevel@tonic-gate 		 * MSI or MSIX (figure out number of vectors available)
7270Sstevel@tonic-gate 		 * FIXED interrupts: just return available interrupts
7280Sstevel@tonic-gate 		 */
7290Sstevel@tonic-gate 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
7300Sstevel@tonic-gate 		    (psm_intr_ops != NULL) &&
7310Sstevel@tonic-gate 		    (pci_get_priority(rdip, hdlp->ih_inum,
7320Sstevel@tonic-gate 		    &priority) == DDI_SUCCESS)) {
7330Sstevel@tonic-gate 			hdlp->ih_pri = priority;
7340Sstevel@tonic-gate 			behavior = hdlp->ih_scratch2;
7350Sstevel@tonic-gate 			(void) (*psm_intr_ops)(rdip, hdlp,
7360Sstevel@tonic-gate 			    PSM_INTR_OP_ALLOC_VECTORS, result);
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 			/* verify behavior flag and take appropriate action */
7390Sstevel@tonic-gate 			if ((behavior == DDI_INTR_ALLOC_STRICT) &&
7400Sstevel@tonic-gate 			    (*(int *)result < hdlp->ih_scratch1)) {
7410Sstevel@tonic-gate 				DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: "
7420Sstevel@tonic-gate 				    "behavior %x, couldn't get enough intrs\n",
7430Sstevel@tonic-gate 				    behavior));
7440Sstevel@tonic-gate 				hdlp->ih_scratch1 = *(int *)result;
7450Sstevel@tonic-gate 				(void) (*psm_intr_ops)(rdip, hdlp,
7460Sstevel@tonic-gate 				    PSM_INTR_OP_FREE_VECTORS, NULL);
7470Sstevel@tonic-gate 				return (DDI_EAGAIN);
7480Sstevel@tonic-gate 			}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
7510Sstevel@tonic-gate 				if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
7520Sstevel@tonic-gate 					msix_p = pci_msix_init(hdlp->ih_dip);
7530Sstevel@tonic-gate 					if (msix_p)
7540Sstevel@tonic-gate 						i_ddi_set_msix(hdlp->ih_dip,
7550Sstevel@tonic-gate 						    msix_p);
7560Sstevel@tonic-gate 				}
7570Sstevel@tonic-gate 				msix_p->msix_intrs_in_use += *(int *)result;
7580Sstevel@tonic-gate 			}
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 		} else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
7610Sstevel@tonic-gate 			/* Figure out if this device supports MASKING */
7620Sstevel@tonic-gate 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
7630Sstevel@tonic-gate 			if (pci_rval == DDI_SUCCESS && pci_status)
7640Sstevel@tonic-gate 				hdlp->ih_cap |= pci_status;
7650Sstevel@tonic-gate 			*(int *)result = 1;	/* DDI_INTR_TYPE_FIXED */
7660Sstevel@tonic-gate 		} else
7670Sstevel@tonic-gate 			return (DDI_FAILURE);
7680Sstevel@tonic-gate 		break;
7690Sstevel@tonic-gate 	case DDI_INTROP_FREE:
7700Sstevel@tonic-gate 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
7710Sstevel@tonic-gate 		    (psm_intr_ops != NULL)) {
7720Sstevel@tonic-gate 			(void) (*psm_intr_ops)(rdip, hdlp,
7730Sstevel@tonic-gate 			    PSM_INTR_OP_FREE_VECTORS, NULL);
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
7760Sstevel@tonic-gate 				msix_p = i_ddi_get_msix(hdlp->ih_dip);
7770Sstevel@tonic-gate 				if (msix_p &&
7780Sstevel@tonic-gate 				    --msix_p->msix_intrs_in_use == 0) {
7790Sstevel@tonic-gate 					pci_msix_fini(msix_p);
7800Sstevel@tonic-gate 					i_ddi_set_msix(hdlp->ih_dip, NULL);
7810Sstevel@tonic-gate 				}
7820Sstevel@tonic-gate 			}
7830Sstevel@tonic-gate 		}
7840Sstevel@tonic-gate 		break;
7850Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
7860Sstevel@tonic-gate 		if (pci_get_priority(rdip, hdlp->ih_inum, &priority) !=
7870Sstevel@tonic-gate 		    DDI_SUCCESS)	/* Get the priority */
7880Sstevel@tonic-gate 			return (DDI_FAILURE);
7890Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: priority = 0x%x\n",
7900Sstevel@tonic-gate 		    priority));
7910Sstevel@tonic-gate 		*(int *)result = priority;
7920Sstevel@tonic-gate 		break;
7930Sstevel@tonic-gate 	case DDI_INTROP_SETPRI:
7940Sstevel@tonic-gate 		/* Validate the interrupt priority passed */
7950Sstevel@tonic-gate 		if (*(int *)result > LOCK_LEVEL)
7960Sstevel@tonic-gate 			return (DDI_FAILURE);
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 		/* Ensure that PSM is all initialized */
7990Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
8000Sstevel@tonic-gate 			return (DDI_FAILURE);
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 		/* Change the priority */
8030Sstevel@tonic-gate 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
8040Sstevel@tonic-gate 		    PSM_FAILURE)
8050Sstevel@tonic-gate 			return (DDI_FAILURE);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 		/* update ispec */
8080Sstevel@tonic-gate 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
8090Sstevel@tonic-gate 		ispec = (struct intrspec *)isp;
8100Sstevel@tonic-gate 		ispec->intrspec_pri = *(int *)result;
8110Sstevel@tonic-gate 		break;
8120Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
8130Sstevel@tonic-gate 		/* update ispec */
8140Sstevel@tonic-gate 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
8150Sstevel@tonic-gate 		ispec = (struct intrspec *)isp;
8160Sstevel@tonic-gate 		ispec->intrspec_func = hdlp->ih_cb_func;
8170Sstevel@tonic-gate 		break;
8180Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
8190Sstevel@tonic-gate 		/* Get the interrupt structure pointer */
8200Sstevel@tonic-gate 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
8210Sstevel@tonic-gate 		ispec = (struct intrspec *)isp;
8220Sstevel@tonic-gate 		ispec->intrspec_func = (uint_t (*)()) 0;
8230Sstevel@tonic-gate 		break;
8240Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
8250Sstevel@tonic-gate 		/*
8260Sstevel@tonic-gate 		 * First check the config space and/or
8270Sstevel@tonic-gate 		 * MSI capability register(s)
8280Sstevel@tonic-gate 		 */
8290Sstevel@tonic-gate 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
8300Sstevel@tonic-gate 			pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
8310Sstevel@tonic-gate 			    &pci_status);
8320Sstevel@tonic-gate 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
8330Sstevel@tonic-gate 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 		/* next check with pcplusmp */
8360Sstevel@tonic-gate 		if (psm_intr_ops != NULL)
8370Sstevel@tonic-gate 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
8380Sstevel@tonic-gate 			    PSM_INTR_OP_GET_CAP, &psm_status);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
8410Sstevel@tonic-gate 		    "psm_status = %x, pci_rval = %x, pci_status = %x\n",
8420Sstevel@tonic-gate 		    psm_rval, psm_status, pci_rval, pci_status));
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
8450Sstevel@tonic-gate 			*(int *)result = 0;
8460Sstevel@tonic-gate 			return (DDI_FAILURE);
8470Sstevel@tonic-gate 		}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 		if (psm_rval == PSM_SUCCESS)
8500Sstevel@tonic-gate 			*(int *)result = psm_status;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		if (pci_rval == DDI_SUCCESS)
8530Sstevel@tonic-gate 			*(int *)result |= pci_status;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
8560Sstevel@tonic-gate 		    *(int *)result));
8570Sstevel@tonic-gate 		break;
8580Sstevel@tonic-gate 	case DDI_INTROP_SETCAP:
8590Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: SETCAP cap=0x%x\n",
8600Sstevel@tonic-gate 		    *(int *)result));
8610Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
8620Sstevel@tonic-gate 			return (DDI_FAILURE);
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
8650Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
8660Sstevel@tonic-gate 			    " returned failure\n"));
8670Sstevel@tonic-gate 			return (DDI_FAILURE);
8680Sstevel@tonic-gate 		}
8690Sstevel@tonic-gate 		break;
8700Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
8710Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: ENABLE\n"));
8720Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
8730Sstevel@tonic-gate 			return (DDI_FAILURE);
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 		if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
8760Sstevel@tonic-gate 		    DDI_SUCCESS)
8770Sstevel@tonic-gate 			return (DDI_FAILURE);
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: ENABLE vector=0x%x\n",
8800Sstevel@tonic-gate 		    hdlp->ih_vector));
8810Sstevel@tonic-gate 		break;
8820Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
8830Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: DISABLE\n"));
8840Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
8850Sstevel@tonic-gate 			return (DDI_FAILURE);
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 		pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
8880Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: DISABLE vector = %x\n",
8890Sstevel@tonic-gate 		    hdlp->ih_vector));
8900Sstevel@tonic-gate 		break;
8910Sstevel@tonic-gate 	case DDI_INTROP_BLOCKENABLE:
8920Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKENABLE\n"));
8930Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
8940Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
8950Sstevel@tonic-gate 			return (DDI_FAILURE);
8960Sstevel@tonic-gate 		}
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 		/* Check if psm_intr_ops is NULL? */
8990Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
9000Sstevel@tonic-gate 			return (DDI_FAILURE);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 		for (i = 0; i < hdlp->ih_scratch1; i++) {
9030Sstevel@tonic-gate 			if (pci_enable_intr(pdip, rdip, hdlp,
9040Sstevel@tonic-gate 			    hdlp->ih_inum + i) != DDI_SUCCESS) {
9050Sstevel@tonic-gate 				DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
9060Sstevel@tonic-gate 				    "pci_enable_intr failed for %d\n", i));
9070Sstevel@tonic-gate 				for (j = 0; j < i; j++)
9080Sstevel@tonic-gate 					pci_disable_intr(pdip, rdip, hdlp,
9090Sstevel@tonic-gate 					    hdlp->ih_inum + j);
9100Sstevel@tonic-gate 				return (DDI_FAILURE);
9110Sstevel@tonic-gate 			}
9120Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKENABLE "
9130Sstevel@tonic-gate 			    "inum %x done\n", hdlp->ih_inum + i));
9140Sstevel@tonic-gate 		}
9150Sstevel@tonic-gate 		break;
9160Sstevel@tonic-gate 	case DDI_INTROP_BLOCKDISABLE:
9170Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKDISABLE\n"));
9180Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
9190Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
9200Sstevel@tonic-gate 			return (DDI_FAILURE);
9210Sstevel@tonic-gate 		}
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 		/* Check if psm_intr_ops is present */
9240Sstevel@tonic-gate 		if (psm_intr_ops == NULL)
9250Sstevel@tonic-gate 			return (DDI_FAILURE);
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 		for (i = 0; i < hdlp->ih_scratch1; i++) {
9280Sstevel@tonic-gate 			pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i);
9290Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKDISABLE "
9300Sstevel@tonic-gate 			    "inum %x done\n", hdlp->ih_inum + i));
9310Sstevel@tonic-gate 		}
9320Sstevel@tonic-gate 		break;
9330Sstevel@tonic-gate 	case DDI_INTROP_SETMASK:
9340Sstevel@tonic-gate 	case DDI_INTROP_CLRMASK:
9350Sstevel@tonic-gate 		/*
9360Sstevel@tonic-gate 		 * First handle in the config space
9370Sstevel@tonic-gate 		 */
9380Sstevel@tonic-gate 		if (intr_op == DDI_INTROP_SETMASK) {
9390Sstevel@tonic-gate 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
9400Sstevel@tonic-gate 				pci_status = pci_msi_set_mask(rdip,
9410Sstevel@tonic-gate 				    hdlp->ih_type, hdlp->ih_inum);
9420Sstevel@tonic-gate 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
9430Sstevel@tonic-gate 				pci_status = pci_intx_set_mask(rdip);
9440Sstevel@tonic-gate 		} else {
9450Sstevel@tonic-gate 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
9460Sstevel@tonic-gate 				pci_status = pci_msi_clr_mask(rdip,
9470Sstevel@tonic-gate 				    hdlp->ih_type, hdlp->ih_inum);
9480Sstevel@tonic-gate 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
9490Sstevel@tonic-gate 				pci_status = pci_intx_clr_mask(rdip);
9500Sstevel@tonic-gate 		}
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 		/* For MSI/X; no need to check with pcplusmp */
9530Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
9540Sstevel@tonic-gate 			return (pci_status);
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 		/* For fixed interrupts only: handle config space first */
9570Sstevel@tonic-gate 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
9580Sstevel@tonic-gate 		    pci_status == DDI_SUCCESS)
9590Sstevel@tonic-gate 			break;
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 		/* For fixed interrupts only: confer with pcplusmp next */
9620Sstevel@tonic-gate 		if (psm_intr_ops != NULL) {
9630Sstevel@tonic-gate 			/* If interrupt is shared; do nothing */
9640Sstevel@tonic-gate 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
9650Sstevel@tonic-gate 			    PSM_INTR_OP_GET_SHARED, &psm_status);
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 			if (psm_rval == PSM_FAILURE || psm_status == 1)
9680Sstevel@tonic-gate 				return (pci_status);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 			/* Now, pcplusmp should try to set/clear the mask */
9710Sstevel@tonic-gate 			if (intr_op == DDI_INTROP_SETMASK)
9720Sstevel@tonic-gate 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
9730Sstevel@tonic-gate 				    PSM_INTR_OP_SET_MASK, NULL);
9740Sstevel@tonic-gate 			else
9750Sstevel@tonic-gate 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
9760Sstevel@tonic-gate 				    PSM_INTR_OP_CLEAR_MASK, NULL);
9770Sstevel@tonic-gate 		}
9780Sstevel@tonic-gate 		return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
9790Sstevel@tonic-gate 	case DDI_INTROP_GETPENDING:
9800Sstevel@tonic-gate 		/*
9810Sstevel@tonic-gate 		 * First check the config space and/or
9820Sstevel@tonic-gate 		 * MSI capability register(s)
9830Sstevel@tonic-gate 		 */
9840Sstevel@tonic-gate 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
9850Sstevel@tonic-gate 			pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
9860Sstevel@tonic-gate 			    hdlp->ih_inum, &pci_status);
9870Sstevel@tonic-gate 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
9880Sstevel@tonic-gate 			pci_rval = pci_intx_get_pending(rdip, &pci_status);
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 		/* On failure; next try with pcplusmp */
9910Sstevel@tonic-gate 		if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
9920Sstevel@tonic-gate 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
9930Sstevel@tonic-gate 			    PSM_INTR_OP_GET_PENDING, &psm_status);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
9960Sstevel@tonic-gate 		    "psm_rval = %x, psm_status = %x, pci_rval = %x, "
9970Sstevel@tonic-gate 		    "pci_status = %x\n", psm_rval, psm_status, pci_rval,
9980Sstevel@tonic-gate 		    pci_status));
9990Sstevel@tonic-gate 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
10000Sstevel@tonic-gate 			*(int *)result = 0;
10010Sstevel@tonic-gate 			return (DDI_FAILURE);
10020Sstevel@tonic-gate 		}
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 		if (psm_rval != PSM_FAILURE)
10050Sstevel@tonic-gate 			*(int *)result = psm_status;
10060Sstevel@tonic-gate 		else if (pci_rval != DDI_FAILURE)
10070Sstevel@tonic-gate 			*(int *)result = pci_status;
10080Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
10090Sstevel@tonic-gate 		    *(int *)result));
10100Sstevel@tonic-gate 		break;
10110Sstevel@tonic-gate 	case DDI_INTROP_NAVAIL:
10120Sstevel@tonic-gate 		if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
10130Sstevel@tonic-gate 		    hdlp->ih_inum, &priority) == DDI_SUCCESS)) {
10140Sstevel@tonic-gate 			/* Priority in the handle not initialized yet */
10150Sstevel@tonic-gate 			hdlp->ih_pri = priority;
10160Sstevel@tonic-gate 			(void) (*psm_intr_ops)(rdip, hdlp,
10170Sstevel@tonic-gate 			    PSM_INTR_OP_NAVAIL_VECTORS, result);
10180Sstevel@tonic-gate 		} else {
10190Sstevel@tonic-gate 			*(int *)result = 1;
10200Sstevel@tonic-gate 		}
10210Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
10220Sstevel@tonic-gate 		    *(int *)result));
10230Sstevel@tonic-gate 		break;
10240Sstevel@tonic-gate 	default:
10250Sstevel@tonic-gate 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
10260Sstevel@tonic-gate 	}
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 	return (DDI_SUCCESS);
10290Sstevel@tonic-gate }
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate static int
10330Sstevel@tonic-gate pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
1034117Sschwartz 	ddi_intr_handle_impl_t *hdlp, uint32_t inum)
10350Sstevel@tonic-gate {
10360Sstevel@tonic-gate 	int		vector;
10370Sstevel@tonic-gate 	struct intrspec	*ispec;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
10400Sstevel@tonic-gate 	    (void *)hdlp, inum));
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	/* Translate the interrupt if needed */
10430Sstevel@tonic-gate 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
10440Sstevel@tonic-gate 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
10450Sstevel@tonic-gate 		ispec->intrspec_vec = inum;
10460Sstevel@tonic-gate 	hdlp->ih_private = (void *)ispec;
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 	/* translate the interrupt if needed */
10490Sstevel@tonic-gate 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
10500Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x vector=%x\n",
10510Sstevel@tonic-gate 	    hdlp->ih_pri, vector));
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 	/* Add the interrupt handler */
10540Sstevel@tonic-gate 	if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
10550Sstevel@tonic-gate 	    DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
10560Sstevel@tonic-gate 	    hdlp->ih_cb_arg2, rdip))
10570Sstevel@tonic-gate 		return (DDI_FAILURE);
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	return (DDI_SUCCESS);
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate static void
10640Sstevel@tonic-gate pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
1065117Sschwartz 	ddi_intr_handle_impl_t *hdlp, uint32_t inum)
10660Sstevel@tonic-gate {
10670Sstevel@tonic-gate 	int		vector;
10680Sstevel@tonic-gate 	struct intrspec	*ispec;
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
10710Sstevel@tonic-gate 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
10720Sstevel@tonic-gate 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
10730Sstevel@tonic-gate 		ispec->intrspec_vec = inum;
10740Sstevel@tonic-gate 	hdlp->ih_private = (void *)ispec;
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	/* translate the interrupt if needed */
10770Sstevel@tonic-gate 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	/* Disable the interrupt handler */
10800Sstevel@tonic-gate 	rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
10810Sstevel@tonic-gate }
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate /*ARGSUSED*/
10850Sstevel@tonic-gate static int
10860Sstevel@tonic-gate pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
10870Sstevel@tonic-gate 	ddi_ctl_enum_t ctlop, void *arg, void *result)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate 	pci_regspec_t *drv_regp;
10900Sstevel@tonic-gate 	uint_t	reglen;
10910Sstevel@tonic-gate 	int	rn;
10920Sstevel@tonic-gate 	int	totreg;
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 	switch (ctlop) {
10950Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
10960Sstevel@tonic-gate 		if (rdip == (dev_info_t *)0)
10970Sstevel@tonic-gate 			return (DDI_FAILURE);
10980Sstevel@tonic-gate 		cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
10990Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
11000Sstevel@tonic-gate 		    ddi_driver_name(rdip),
11010Sstevel@tonic-gate 		    ddi_get_instance(rdip));
11020Sstevel@tonic-gate 		return (DDI_SUCCESS);
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
11050Sstevel@tonic-gate 		return (pci_initchild((dev_info_t *)arg));
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
11080Sstevel@tonic-gate 		return (pci_removechild((dev_info_t *)arg));
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 	case DDI_CTLOPS_NINTRS:
11110Sstevel@tonic-gate 		if (ddi_get_parent_data(rdip))
11120Sstevel@tonic-gate 			*(int *)result = 1;
11130Sstevel@tonic-gate 		else
11140Sstevel@tonic-gate 			*(int *)result = 0;
11150Sstevel@tonic-gate 		return (DDI_SUCCESS);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	case DDI_CTLOPS_XLATE_INTRS:
11180Sstevel@tonic-gate 		return (DDI_SUCCESS);
11190Sstevel@tonic-gate 
11200Sstevel@tonic-gate 	case DDI_CTLOPS_SIDDEV:
11210Sstevel@tonic-gate 		return (DDI_SUCCESS);
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 	case DDI_CTLOPS_REGSIZE:
11240Sstevel@tonic-gate 	case DDI_CTLOPS_NREGS:
11250Sstevel@tonic-gate 		if (rdip == (dev_info_t *)0)
11260Sstevel@tonic-gate 			return (DDI_FAILURE);
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 		*(int *)result = 0;
11290Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
11300Sstevel@tonic-gate 				DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
11310Sstevel@tonic-gate 				&reglen) != DDI_PROP_SUCCESS) {
11320Sstevel@tonic-gate 			return (DDI_FAILURE);
11330Sstevel@tonic-gate 		}
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 		totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t);
11360Sstevel@tonic-gate 		if (ctlop == DDI_CTLOPS_NREGS)
11370Sstevel@tonic-gate 			*(int *)result = totreg;
11380Sstevel@tonic-gate 		else if (ctlop == DDI_CTLOPS_REGSIZE) {
11390Sstevel@tonic-gate 			rn = *(int *)arg;
11400Sstevel@tonic-gate 			if (rn >= totreg) {
11410Sstevel@tonic-gate 				ddi_prop_free(drv_regp);
11420Sstevel@tonic-gate 				return (DDI_FAILURE);
11430Sstevel@tonic-gate 			}
11440Sstevel@tonic-gate 			*(off_t *)result = drv_regp[rn].pci_size_low;
11450Sstevel@tonic-gate 		}
11460Sstevel@tonic-gate 		ddi_prop_free(drv_regp);
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 		return (DDI_SUCCESS);
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	case DDI_CTLOPS_POWER: {
11510Sstevel@tonic-gate 		power_req_t	*reqp = (power_req_t *)arg;
11520Sstevel@tonic-gate 		/*
11530Sstevel@tonic-gate 		 * We currently understand reporting of PCI_PM_IDLESPEED
11540Sstevel@tonic-gate 		 * capability. Everything else is passed up.
11550Sstevel@tonic-gate 		 */
11560Sstevel@tonic-gate 		if ((reqp->request_type == PMR_REPORT_PMCAP) &&
11570Sstevel@tonic-gate 		    (reqp->req.report_pmcap_req.cap ==  PCI_PM_IDLESPEED)) {
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 			return (DDI_SUCCESS);
11600Sstevel@tonic-gate 		}
11610Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
11620Sstevel@tonic-gate 	}
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	default:
11650Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
11660Sstevel@tonic-gate 	}
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	/* NOTREACHED */
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate }
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate /*
11730Sstevel@tonic-gate  * Assign the address portion of the node name
11740Sstevel@tonic-gate  */
11750Sstevel@tonic-gate static int
11760Sstevel@tonic-gate pci_name_child(dev_info_t *child, char *name, int namelen)
11770Sstevel@tonic-gate {
11780Sstevel@tonic-gate 	pci_regspec_t *pci_rp;
11790Sstevel@tonic-gate 	char **unit_addr;
11800Sstevel@tonic-gate 	int dev, func, length;
11810Sstevel@tonic-gate 	uint_t n;
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	if (ndi_dev_is_persistent_node(child) == 0) {
11840Sstevel@tonic-gate 		/*
11850Sstevel@tonic-gate 		 * For .conf node, use "unit-address" property
11860Sstevel@tonic-gate 		 */
11870Sstevel@tonic-gate 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
11880Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
11890Sstevel@tonic-gate 		    DDI_PROP_SUCCESS) {
11900Sstevel@tonic-gate 			cmn_err(CE_WARN,
11910Sstevel@tonic-gate 			    "cannot find unit-address in %s.conf",
11920Sstevel@tonic-gate 			    ddi_get_name(child));
11930Sstevel@tonic-gate 			return (DDI_FAILURE);
11940Sstevel@tonic-gate 		}
11950Sstevel@tonic-gate 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
11960Sstevel@tonic-gate 			cmn_err(CE_WARN, "unit-address property in %s.conf"
11970Sstevel@tonic-gate 			    " not well-formed", ddi_get_name(child));
11980Sstevel@tonic-gate 			ddi_prop_free(unit_addr);
11990Sstevel@tonic-gate 			return (DDI_FAILURE);
12000Sstevel@tonic-gate 		}
12010Sstevel@tonic-gate 		(void) snprintf(name, namelen, "%s", *unit_addr);
12020Sstevel@tonic-gate 		ddi_prop_free(unit_addr);
12030Sstevel@tonic-gate 		return (DDI_SUCCESS);
12040Sstevel@tonic-gate 	}
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
12070Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
12080Sstevel@tonic-gate 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
12090Sstevel@tonic-gate 		cmn_err(CE_WARN, "cannot find reg property in %s",
12100Sstevel@tonic-gate 		    ddi_get_name(child));
12110Sstevel@tonic-gate 		return (DDI_FAILURE);
12120Sstevel@tonic-gate 	}
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	/* copy the device identifications */
12150Sstevel@tonic-gate 	dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
12160Sstevel@tonic-gate 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	/*
12190Sstevel@tonic-gate 	 * free the memory allocated by ddi_prop_lookup_int_array
12200Sstevel@tonic-gate 	 */
12210Sstevel@tonic-gate 	ddi_prop_free(pci_rp);
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate 	if (func != 0) {
12240Sstevel@tonic-gate 		(void) snprintf(name, namelen, "%x,%x", dev, func);
12250Sstevel@tonic-gate 	} else {
12260Sstevel@tonic-gate 		(void) snprintf(name, namelen, "%x", dev);
12270Sstevel@tonic-gate 	}
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 	return (DDI_SUCCESS);
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate static int
12330Sstevel@tonic-gate pci_initchild(dev_info_t *child)
12340Sstevel@tonic-gate {
12350Sstevel@tonic-gate 	struct ddi_parent_private_data *pdptr;
12360Sstevel@tonic-gate 	char name[80];
1237*354Smyers 	ddi_acc_handle_t config_handle;
1238*354Smyers 	ushort_t command_preserve, command;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	if (pci_name_child(child, name, 80) != DDI_SUCCESS) {
12410Sstevel@tonic-gate 		return (DDI_FAILURE);
12420Sstevel@tonic-gate 	}
12430Sstevel@tonic-gate 	ddi_set_name_addr(child, name);
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	/*
12460Sstevel@tonic-gate 	 * Pseudo nodes indicate a prototype node with per-instance
12470Sstevel@tonic-gate 	 * properties to be merged into the real h/w device node.
12480Sstevel@tonic-gate 	 * The interpretation of the unit-address is DD[,F]
12490Sstevel@tonic-gate 	 * where DD is the device id and F is the function.
12500Sstevel@tonic-gate 	 */
12510Sstevel@tonic-gate 	if (ndi_dev_is_persistent_node(child) == 0) {
12520Sstevel@tonic-gate 		extern int pci_allow_pseudo_children;
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
12550Sstevel@tonic-gate 
12560Sstevel@tonic-gate 		/*
12570Sstevel@tonic-gate 		 * Try to merge the properties from this prototype
12580Sstevel@tonic-gate 		 * node into real h/w nodes.
12590Sstevel@tonic-gate 		 */
12600Sstevel@tonic-gate 		if (ndi_merge_node(child, pci_name_child) == DDI_SUCCESS) {
12610Sstevel@tonic-gate 			/*
12620Sstevel@tonic-gate 			 * Merged ok - return failure to remove the node.
12630Sstevel@tonic-gate 			 */
12640Sstevel@tonic-gate 			ddi_set_name_addr(child, NULL);
12650Sstevel@tonic-gate 			return (DDI_FAILURE);
12660Sstevel@tonic-gate 		}
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 		/* workaround for ddivs to run under PCI */
12690Sstevel@tonic-gate 		if (pci_allow_pseudo_children) {
12700Sstevel@tonic-gate 			/*
12710Sstevel@tonic-gate 			 * If the "interrupts" property doesn't exist,
12720Sstevel@tonic-gate 			 * this must be the ddivs no-intr case, and it returns
12730Sstevel@tonic-gate 			 * DDI_SUCCESS instead of DDI_FAILURE.
12740Sstevel@tonic-gate 			 */
12750Sstevel@tonic-gate 			if (ddi_prop_get_int(DDI_DEV_T_ANY, child,
12760Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, "interrupts", -1) == -1)
12770Sstevel@tonic-gate 				return (DDI_SUCCESS);
12780Sstevel@tonic-gate 			/*
12790Sstevel@tonic-gate 			 * Create the ddi_parent_private_data for a pseudo
12800Sstevel@tonic-gate 			 * child.
12810Sstevel@tonic-gate 			 */
12820Sstevel@tonic-gate 			pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
12830Sstevel@tonic-gate 			    (sizeof (struct ddi_parent_private_data) +
12840Sstevel@tonic-gate 			    sizeof (struct intrspec)), KM_SLEEP);
12850Sstevel@tonic-gate 			pdptr->par_intr = (struct intrspec *)(pdptr + 1);
12860Sstevel@tonic-gate 			pdptr->par_nintr = 1;
12870Sstevel@tonic-gate 			ddi_set_parent_data(child, pdptr);
12880Sstevel@tonic-gate 			return (DDI_SUCCESS);
12890Sstevel@tonic-gate 		}
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 		/*
12920Sstevel@tonic-gate 		 * The child was not merged into a h/w node,
12930Sstevel@tonic-gate 		 * but there's not much we can do with it other
12940Sstevel@tonic-gate 		 * than return failure to cause the node to be removed.
12950Sstevel@tonic-gate 		 */
12960Sstevel@tonic-gate 		cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
12970Sstevel@tonic-gate 		    ddi_get_name(child), ddi_get_name_addr(child),
12980Sstevel@tonic-gate 		    ddi_get_name(child));
12990Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
13000Sstevel@tonic-gate 		return (DDI_NOT_WELL_FORMED);
13010Sstevel@tonic-gate 	}
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
13040Sstevel@tonic-gate 	    "interrupts", -1) != -1) {
13050Sstevel@tonic-gate 		pdptr = (struct ddi_parent_private_data *)
13060Sstevel@tonic-gate 		    kmem_zalloc((sizeof (struct ddi_parent_private_data) +
13070Sstevel@tonic-gate 		    sizeof (struct intrspec)), KM_SLEEP);
13080Sstevel@tonic-gate 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
13090Sstevel@tonic-gate 		pdptr->par_nintr = 1;
13100Sstevel@tonic-gate 		ddi_set_parent_data(child, pdptr);
13110Sstevel@tonic-gate 	} else
13120Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
13130Sstevel@tonic-gate 
1314*354Smyers 	/*
1315*354Smyers 	 * initialize command register
1316*354Smyers 	 */
1317*354Smyers 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
1318*354Smyers 		return (DDI_FAILURE);
1319*354Smyers 
1320*354Smyers 	/*
1321*354Smyers 	 * Support for the "command-preserve" property.
1322*354Smyers 	 */
1323*354Smyers 	command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
1324*354Smyers 						DDI_PROP_DONTPASS,
1325*354Smyers 						"command-preserve", 0);
1326*354Smyers 	command = pci_config_get16(config_handle, PCI_CONF_COMM);
1327*354Smyers 	command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
1328*354Smyers 	command |= (pci_command_default & ~command_preserve);
1329*354Smyers 	pci_config_put16(config_handle, PCI_CONF_COMM, command);
1330*354Smyers 
1331*354Smyers 	pci_config_teardown(&config_handle);
13320Sstevel@tonic-gate 	return (DDI_SUCCESS);
13330Sstevel@tonic-gate }
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate static int
13360Sstevel@tonic-gate pci_removechild(dev_info_t *dip)
13370Sstevel@tonic-gate {
13380Sstevel@tonic-gate 	struct ddi_parent_private_data *pdptr;
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 	if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
13410Sstevel@tonic-gate 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
13420Sstevel@tonic-gate 		ddi_set_parent_data(dip, NULL);
13430Sstevel@tonic-gate 	}
13440Sstevel@tonic-gate 	ddi_set_name_addr(dip, NULL);
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 	/*
13470Sstevel@tonic-gate 	 * Strip the node to properly convert it back to prototype form
13480Sstevel@tonic-gate 	 */
13490Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
13500Sstevel@tonic-gate 
13510Sstevel@tonic-gate 	impl_rem_dev_props(dip);
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 	return (DDI_SUCCESS);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate /*
13580Sstevel@tonic-gate  * These are the get and put functions to be shared with drivers. The
13590Sstevel@tonic-gate  * mutex locking is done inside the functions referenced, rather than
13600Sstevel@tonic-gate  * here, and is thus shared across PCI child drivers and any other
13610Sstevel@tonic-gate  * consumers of PCI config space (such as the ACPI subsystem).
13620Sstevel@tonic-gate  *
13630Sstevel@tonic-gate  * The configuration space addresses come in as pointers.  This is fine on
13640Sstevel@tonic-gate  * a 32-bit system, where the VM space and configuration space are the same
13650Sstevel@tonic-gate  * size.  It's not such a good idea on a 64-bit system, where memory
13660Sstevel@tonic-gate  * addresses are twice as large as configuration space addresses.  At some
13670Sstevel@tonic-gate  * point in the call tree we need to take a stand and say "you are 32-bit
13680Sstevel@tonic-gate  * from this time forth", and this seems like a nice self-contained place.
13690Sstevel@tonic-gate  */
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate static uint8_t
13720Sstevel@tonic-gate pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr)
13730Sstevel@tonic-gate {
13740Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
13750Sstevel@tonic-gate 	uint8_t	rval;
13760Sstevel@tonic-gate 	int reg;
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 	rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
13850Sstevel@tonic-gate 	    reg);
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate 	return (rval);
13880Sstevel@tonic-gate }
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate static void
13910Sstevel@tonic-gate pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
13920Sstevel@tonic-gate 	uint8_t *dev_addr, size_t repcount, uint_t flags)
13930Sstevel@tonic-gate {
13940Sstevel@tonic-gate 	uint8_t *h, *d;
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 	h = host_addr;
13970Sstevel@tonic-gate 	d = dev_addr;
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
14000Sstevel@tonic-gate 		for (; repcount; repcount--)
14010Sstevel@tonic-gate 			*h++ = pci_config_rd8(hdlp, d++);
14020Sstevel@tonic-gate 	else
14030Sstevel@tonic-gate 		for (; repcount; repcount--)
14040Sstevel@tonic-gate 			*h++ = pci_config_rd8(hdlp, d);
14050Sstevel@tonic-gate }
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate static uint16_t
14080Sstevel@tonic-gate pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr)
14090Sstevel@tonic-gate {
14100Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
14110Sstevel@tonic-gate 	uint16_t rval;
14120Sstevel@tonic-gate 	int reg;
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 	rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
14210Sstevel@tonic-gate 	    reg);
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 	return (rval);
14240Sstevel@tonic-gate }
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate static void
14270Sstevel@tonic-gate pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
14280Sstevel@tonic-gate 	uint16_t *dev_addr, size_t repcount, uint_t flags)
14290Sstevel@tonic-gate {
14300Sstevel@tonic-gate 	uint16_t *h, *d;
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	h = host_addr;
14330Sstevel@tonic-gate 	d = dev_addr;
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
14360Sstevel@tonic-gate 		for (; repcount; repcount--)
14370Sstevel@tonic-gate 			*h++ = pci_config_rd16(hdlp, d++);
14380Sstevel@tonic-gate 	else
14390Sstevel@tonic-gate 		for (; repcount; repcount--)
14400Sstevel@tonic-gate 			*h++ = pci_config_rd16(hdlp, d);
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate static uint32_t
14440Sstevel@tonic-gate pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr)
14450Sstevel@tonic-gate {
14460Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
14470Sstevel@tonic-gate 	uint32_t rval;
14480Sstevel@tonic-gate 	int reg;
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 	rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum,
14570Sstevel@tonic-gate 	    cfp->c_funcnum, reg);
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 	return (rval);
14600Sstevel@tonic-gate }
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate static void
14630Sstevel@tonic-gate pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
14640Sstevel@tonic-gate 	uint32_t *dev_addr, size_t repcount, uint_t flags)
14650Sstevel@tonic-gate {
14660Sstevel@tonic-gate 	uint32_t *h, *d;
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate 	h = host_addr;
14690Sstevel@tonic-gate 	d = dev_addr;
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
14720Sstevel@tonic-gate 		for (; repcount; repcount--)
14730Sstevel@tonic-gate 			*h++ = pci_config_rd32(hdlp, d++);
14740Sstevel@tonic-gate 	else
14750Sstevel@tonic-gate 		for (; repcount; repcount--)
14760Sstevel@tonic-gate 			*h++ = pci_config_rd32(hdlp, d);
14770Sstevel@tonic-gate }
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 
14800Sstevel@tonic-gate static void
14810Sstevel@tonic-gate pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
14820Sstevel@tonic-gate {
14830Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
14840Sstevel@tonic-gate 	int reg;
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	(*pci_putb_func)(cfp->c_busnum, cfp->c_devnum,
14930Sstevel@tonic-gate 	    cfp->c_funcnum, reg, value);
14940Sstevel@tonic-gate }
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate static void
14970Sstevel@tonic-gate pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
14980Sstevel@tonic-gate 	uint8_t *dev_addr, size_t repcount, uint_t flags)
14990Sstevel@tonic-gate {
15000Sstevel@tonic-gate 	uint8_t *h, *d;
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate 	h = host_addr;
15030Sstevel@tonic-gate 	d = dev_addr;
15040Sstevel@tonic-gate 
15050Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
15060Sstevel@tonic-gate 		for (; repcount; repcount--)
15070Sstevel@tonic-gate 			pci_config_wr8(hdlp, d++, *h++);
15080Sstevel@tonic-gate 	else
15090Sstevel@tonic-gate 		for (; repcount; repcount--)
15100Sstevel@tonic-gate 			pci_config_wr8(hdlp, d, *h++);
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate static void
15140Sstevel@tonic-gate pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
15150Sstevel@tonic-gate {
15160Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
15170Sstevel@tonic-gate 	int reg;
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 	(*pci_putw_func)(cfp->c_busnum, cfp->c_devnum,
15260Sstevel@tonic-gate 	    cfp->c_funcnum, reg, value);
15270Sstevel@tonic-gate }
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate static void
15300Sstevel@tonic-gate pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
15310Sstevel@tonic-gate 	uint16_t *dev_addr, size_t repcount, uint_t flags)
15320Sstevel@tonic-gate {
15330Sstevel@tonic-gate 	uint16_t *h, *d;
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 	h = host_addr;
15360Sstevel@tonic-gate 	d = dev_addr;
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
15390Sstevel@tonic-gate 		for (; repcount; repcount--)
15400Sstevel@tonic-gate 			pci_config_wr16(hdlp, d++, *h++);
15410Sstevel@tonic-gate 	else
15420Sstevel@tonic-gate 		for (; repcount; repcount--)
15430Sstevel@tonic-gate 			pci_config_wr16(hdlp, d, *h++);
15440Sstevel@tonic-gate }
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate static void
15470Sstevel@tonic-gate pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value)
15480Sstevel@tonic-gate {
15490Sstevel@tonic-gate 	pci_acc_cfblk_t *cfp;
15500Sstevel@tonic-gate 	int reg;
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	ASSERT64(((uintptr_t)addr >> 32) == 0);
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	reg = (int)(uintptr_t)addr;
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 	(*pci_putl_func)(cfp->c_busnum, cfp->c_devnum,
15590Sstevel@tonic-gate 	    cfp->c_funcnum, reg, value);
15600Sstevel@tonic-gate }
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate static void
15630Sstevel@tonic-gate pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
15640Sstevel@tonic-gate 	uint32_t *dev_addr, size_t repcount, uint_t flags)
15650Sstevel@tonic-gate {
15660Sstevel@tonic-gate 	uint32_t *h, *d;
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate 	h = host_addr;
15690Sstevel@tonic-gate 	d = dev_addr;
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR)
15720Sstevel@tonic-gate 		for (; repcount; repcount--)
15730Sstevel@tonic-gate 			pci_config_wr32(hdlp, d++, *h++);
15740Sstevel@tonic-gate 	else
15750Sstevel@tonic-gate 		for (; repcount; repcount--)
15760Sstevel@tonic-gate 			pci_config_wr32(hdlp, d, *h++);
15770Sstevel@tonic-gate }
15780Sstevel@tonic-gate 
15790Sstevel@tonic-gate static uint64_t
15800Sstevel@tonic-gate pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr)
15810Sstevel@tonic-gate {
15820Sstevel@tonic-gate 	uint32_t lw_val;
15830Sstevel@tonic-gate 	uint32_t hi_val;
15840Sstevel@tonic-gate 	uint32_t *dp;
15850Sstevel@tonic-gate 	uint64_t val;
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	dp = (uint32_t *)addr;
15880Sstevel@tonic-gate 	lw_val = pci_config_rd32(hdlp, dp);
15890Sstevel@tonic-gate 	dp++;
15900Sstevel@tonic-gate 	hi_val = pci_config_rd32(hdlp, dp);
15910Sstevel@tonic-gate 	val = ((uint64_t)hi_val << 32) | lw_val;
15920Sstevel@tonic-gate 	return (val);
15930Sstevel@tonic-gate }
15940Sstevel@tonic-gate 
15950Sstevel@tonic-gate static void
15960Sstevel@tonic-gate pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value)
15970Sstevel@tonic-gate {
15980Sstevel@tonic-gate 	uint32_t lw_val;
15990Sstevel@tonic-gate 	uint32_t hi_val;
16000Sstevel@tonic-gate 	uint32_t *dp;
16010Sstevel@tonic-gate 
16020Sstevel@tonic-gate 	dp = (uint32_t *)addr;
16030Sstevel@tonic-gate 	lw_val = (uint32_t)(value & 0xffffffff);
16040Sstevel@tonic-gate 	hi_val = (uint32_t)(value >> 32);
16050Sstevel@tonic-gate 	pci_config_wr32(hdlp, dp, lw_val);
16060Sstevel@tonic-gate 	dp++;
16070Sstevel@tonic-gate 	pci_config_wr32(hdlp, dp, hi_val);
16080Sstevel@tonic-gate }
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate static void
16110Sstevel@tonic-gate pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
16120Sstevel@tonic-gate 	uint64_t *dev_addr, size_t repcount, uint_t flags)
16130Sstevel@tonic-gate {
16140Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR) {
16150Sstevel@tonic-gate 		for (; repcount; repcount--)
16160Sstevel@tonic-gate 			*host_addr++ = pci_config_rd64(hdlp, dev_addr++);
16170Sstevel@tonic-gate 	} else {
16180Sstevel@tonic-gate 		for (; repcount; repcount--)
16190Sstevel@tonic-gate 			*host_addr++ = pci_config_rd64(hdlp, dev_addr);
16200Sstevel@tonic-gate 	}
16210Sstevel@tonic-gate }
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate static void
16240Sstevel@tonic-gate pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
16250Sstevel@tonic-gate 	uint64_t *dev_addr, size_t repcount, uint_t flags)
16260Sstevel@tonic-gate {
16270Sstevel@tonic-gate 	if (flags == DDI_DEV_AUTOINCR) {
16280Sstevel@tonic-gate 		for (; repcount; repcount--)
16290Sstevel@tonic-gate 			pci_config_wr64(hdlp, host_addr++, *dev_addr++);
16300Sstevel@tonic-gate 	} else {
16310Sstevel@tonic-gate 		for (; repcount; repcount--)
16320Sstevel@tonic-gate 			pci_config_wr64(hdlp, host_addr++, *dev_addr);
16330Sstevel@tonic-gate 	}
16340Sstevel@tonic-gate }
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate /*
16370Sstevel@tonic-gate  * When retrofitting this module for pci_tools, functions such as open, close,
16380Sstevel@tonic-gate  * and ioctl are now pulled into this module.  Before this, the functions in
16390Sstevel@tonic-gate  * the pcihp module were referenced directly.  Now they are called or
16400Sstevel@tonic-gate  * referenced through the pcihp cb_ops structure from functions in this module.
16410Sstevel@tonic-gate  */
16420Sstevel@tonic-gate 
16430Sstevel@tonic-gate static int
16440Sstevel@tonic-gate pci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
16450Sstevel@tonic-gate {
16460Sstevel@tonic-gate 	return ((pcihp_cb_ops.cb_open)(devp, flags, otyp, credp));
16470Sstevel@tonic-gate }
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate static int
16500Sstevel@tonic-gate pci_close(dev_t dev, int flags, int otyp, cred_t *credp)
16510Sstevel@tonic-gate {
16520Sstevel@tonic-gate 	return ((pcihp_cb_ops.cb_close)(dev, flags, otyp, credp));
16530Sstevel@tonic-gate }
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate static int
16560Sstevel@tonic-gate pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
16570Sstevel@tonic-gate 	int *rvalp)
16580Sstevel@tonic-gate {
16590Sstevel@tonic-gate 	int rv = ENOTTY;
16600Sstevel@tonic-gate 
1661117Sschwartz 	minor_t minor = getminor(dev);
16620Sstevel@tonic-gate 
1663117Sschwartz 	switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
1664117Sschwartz 	case PCIHP_DEVCTL_MINOR:
1665117Sschwartz 		if (IS_DEVCTL(cmd))
1666117Sschwartz 			rv = (pcihp_cb_ops.cb_ioctl)(dev, cmd, arg, mode,
1667117Sschwartz 			    credp, rvalp);
1668117Sschwartz 		break;
16690Sstevel@tonic-gate 
1670117Sschwartz 	case PCI_TOOL_REG_MINOR_NUM:
1671117Sschwartz 
16720Sstevel@tonic-gate 		switch (cmd) {
16730Sstevel@tonic-gate 		case PCITOOL_DEVICE_SET_REG:
16740Sstevel@tonic-gate 		case PCITOOL_DEVICE_GET_REG:
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate 			/* Require full privileges. */
16770Sstevel@tonic-gate 			if (secpolicy_kmdb(credp))
16780Sstevel@tonic-gate 				rv = EPERM;
16790Sstevel@tonic-gate 			else
16800Sstevel@tonic-gate 				rv = pcitool_dev_reg_ops(
16810Sstevel@tonic-gate 				    dev, (void *)arg, cmd, mode);
16820Sstevel@tonic-gate 			break;
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 		case PCITOOL_NEXUS_SET_REG:
16850Sstevel@tonic-gate 		case PCITOOL_NEXUS_GET_REG:
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 			/* Require full privileges. */
16880Sstevel@tonic-gate 			if (secpolicy_kmdb(credp))
16890Sstevel@tonic-gate 				rv = EPERM;
16900Sstevel@tonic-gate 			else
16910Sstevel@tonic-gate 				rv = pcitool_bus_reg_ops(
16920Sstevel@tonic-gate 				    dev, (void *)arg, cmd, mode);
16930Sstevel@tonic-gate 			break;
1694117Sschwartz 		}
1695117Sschwartz 		break;
16960Sstevel@tonic-gate 
1697117Sschwartz 	case PCI_TOOL_INTR_MINOR_NUM:
1698117Sschwartz 
1699117Sschwartz 		switch (cmd) {
17000Sstevel@tonic-gate 		case PCITOOL_DEVICE_SET_INTR:
17010Sstevel@tonic-gate 
1702117Sschwartz 			/* Require PRIV_SYS_RES_CONFIG, same as psradm */
17030Sstevel@tonic-gate 			if (secpolicy_ponline(credp)) {
17040Sstevel@tonic-gate 				rv = EPERM;
17050Sstevel@tonic-gate 				break;
17060Sstevel@tonic-gate 			}
17070Sstevel@tonic-gate 
17080Sstevel@tonic-gate 		/*FALLTHRU*/
17090Sstevel@tonic-gate 		/* These require no special privileges. */
17100Sstevel@tonic-gate 		case PCITOOL_DEVICE_GET_INTR:
17110Sstevel@tonic-gate 		case PCITOOL_DEVICE_NUM_INTR:
17120Sstevel@tonic-gate 			rv = pcitool_intr_admn(dev, (void *)arg, cmd, mode);
17130Sstevel@tonic-gate 			break;
1714117Sschwartz 		}
1715117Sschwartz 		break;
17160Sstevel@tonic-gate 
1717117Sschwartz 	default:
1718117Sschwartz 		break;
17190Sstevel@tonic-gate 	}
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	return (rv);
17220Sstevel@tonic-gate }
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate static int
17250Sstevel@tonic-gate pci_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
1726117Sschwartz 	int flags, char *name, caddr_t valuep, int *lengthp)
17270Sstevel@tonic-gate {
17280Sstevel@tonic-gate 	return ((pcihp_cb_ops.cb_prop_op)(dev, dip, prop_op, flags,
17290Sstevel@tonic-gate 	    name, valuep, lengthp));
17300Sstevel@tonic-gate }
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate static int
17330Sstevel@tonic-gate pci_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
17340Sstevel@tonic-gate {
17350Sstevel@tonic-gate 	return (pcihp_info(dip, cmd, arg, result));
17360Sstevel@tonic-gate }
1737