xref: /onnv-gate/usr/src/uts/sun4u/io/pci/pci_tools.c (revision 10053:79ff8cfc9153)
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
54397Sschwartz  * Common Development and Distribution License (the "License").
64397Sschwartz  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*10053SEvan.Yan@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
26117Sschwartz #include <sys/stat.h>
27117Sschwartz #include <sys/sunddi.h>
28117Sschwartz #include <sys/param.h>
29117Sschwartz 
300Sstevel@tonic-gate #include <sys/sysmacros.h>
310Sstevel@tonic-gate #include <sys/machsystm.h>
320Sstevel@tonic-gate #include <sys/promif.h>
330Sstevel@tonic-gate #include <sys/cpuvar.h>
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
360Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <sys/pci_tools.h>
39777Sschwartz #include <sys/pci/pci_tools_ext.h>
40777Sschwartz 
41777Sschwartz /*
42777Sschwartz  * Number of interrupts supported per PCI bus.
43777Sschwartz  */
44777Sschwartz #define	PCI_MAX_INO		0x3f
45777Sschwartz 
46777Sschwartz /*
47777Sschwartz  * PCI Space definitions.
48777Sschwartz  */
49777Sschwartz #define	PCI_CONFIG_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
50777Sschwartz #define	PCI_IO_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_IO))
51777Sschwartz #define	PCI_MEM_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM32))
52777Sschwartz #define	PCI_MEM64_RANGE_BANK	(PCI_REG_ADDR_G(PCI_ADDR_MEM64))
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
550Sstevel@tonic-gate  * Extract 64 bit parent or size values from 32 bit cells of
560Sstevel@tonic-gate  * pci_ranges_t property.
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  * Only bits 42-32 are relevant in parent_high.
590Sstevel@tonic-gate  */
600Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP(ranges, bank) \
610Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].parent_high & 0x7ff)) << 32) | \
620Sstevel@tonic-gate 	ranges[bank].parent_low)
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	PCI_GET_RANGE_PROP_SIZE(ranges, bank) \
650Sstevel@tonic-gate 	((((uint64_t)(ranges[bank].size_high)) << 32) | \
660Sstevel@tonic-gate 	ranges[bank].size_low)
670Sstevel@tonic-gate 
68117Sschwartz #define	PCI_BAR_OFFSET(x)	(pci_bars[x.barnum])
69117Sschwartz 
700Sstevel@tonic-gate /* Big and little endian as boolean values. */
710Sstevel@tonic-gate #define	BE B_TRUE
720Sstevel@tonic-gate #define	LE B_FALSE
730Sstevel@tonic-gate 
740Sstevel@tonic-gate #define	SUCCESS	0
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */
770Sstevel@tonic-gate typedef union {
780Sstevel@tonic-gate 	uint64_t u64;
790Sstevel@tonic-gate 	uint32_t u32;
800Sstevel@tonic-gate 	uint16_t u16;
810Sstevel@tonic-gate 	uint8_t u8;
820Sstevel@tonic-gate } peek_poke_value_t;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate  * Offsets of BARS in config space.  First entry of 0 means config space.
860Sstevel@tonic-gate  * Entries here correlate to pcitool_bars_t enumerated type.
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate static uint8_t pci_bars[] = {
890Sstevel@tonic-gate 	0x0,
900Sstevel@tonic-gate 	PCI_CONF_BASE0,
910Sstevel@tonic-gate 	PCI_CONF_BASE1,
920Sstevel@tonic-gate 	PCI_CONF_BASE2,
930Sstevel@tonic-gate 	PCI_CONF_BASE3,
940Sstevel@tonic-gate 	PCI_CONF_BASE4,
950Sstevel@tonic-gate 	PCI_CONF_BASE5,
960Sstevel@tonic-gate 	PCI_CONF_ROM
970Sstevel@tonic-gate };
980Sstevel@tonic-gate 
990Sstevel@tonic-gate /*LINTLIBRARY*/
1000Sstevel@tonic-gate 
101117Sschwartz static int pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
102117Sschwartz     uint64_t paddr, uint64_t *value_p);
103117Sschwartz static int pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
104117Sschwartz     uint64_t paddr, uint64_t value);
105117Sschwartz static int pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
106117Sschwartz     uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
107117Sschwartz     uint32_t *pcitool_status);
108117Sschwartz static int pcitool_validate_barnum_bdf(pcitool_reg_t *prg);
109117Sschwartz static int pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg,
110117Sschwartz     uint64_t config_base_addr, uint64_t config_max_addr, uint64_t *bar,
111117Sschwartz     boolean_t *is_io_space);
112117Sschwartz static int pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg,
113117Sschwartz     uint64_t base_addr, uint64_t max_addr, uint8_t size, boolean_t write_flag);
114117Sschwartz static int pcitool_intr_get_max_ino(uint32_t *arg, int mode);
115117Sschwartz static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
116117Sschwartz static int pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p);
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate extern int pci_do_phys_peek(size_t, uint64_t, uint64_t *, int);
1190Sstevel@tonic-gate extern int pci_do_phys_poke(size_t, uint64_t, uint64_t *, int);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_peek
1230Sstevel@tonic-gate  *
1240Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1250Sstevel@tonic-gate  * Size is 1, 2, 4 or 8 bytes.
1260Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1270Sstevel@tonic-gate  * value_p is where the value is returned.
1280Sstevel@tonic-gate  */
1290Sstevel@tonic-gate static int
pcitool_phys_peek(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t * value_p)130117Sschwartz pcitool_phys_peek(pci_t *pci_p, boolean_t type, size_t size,
131117Sschwartz 	uint64_t paddr, uint64_t *value_p)
1320Sstevel@tonic-gate {
1330Sstevel@tonic-gate 	on_trap_data_t otd;
1340Sstevel@tonic-gate 	int err = DDI_SUCCESS;
135117Sschwartz 	peek_poke_value_t peek_value;
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	/* Set up trap handling to make the access safe. */
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	/*
1440Sstevel@tonic-gate 	 * on_trap works like setjmp.
1450Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
1460Sstevel@tonic-gate 	 * but to call peek_fault instead.
1470Sstevel@tonic-gate 	 * Call pci_do_phys_peek after trap handling is setup.
1480Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
1490Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
1500Sstevel@tonic-gate 	 */
1510Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
1520Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&peek_fault;
1530Sstevel@tonic-gate 		err = pci_do_phys_peek(size, paddr, &peek_value.u64, type);
1540Sstevel@tonic-gate 	} else {
1550Sstevel@tonic-gate 		err = DDI_FAILURE;
1560Sstevel@tonic-gate 	}
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
1590Sstevel@tonic-gate 	no_trap();
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	if (err != DDI_FAILURE) {
1620Sstevel@tonic-gate 		switch (size) {
1630Sstevel@tonic-gate 		case 8:
1640Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u64;
1650Sstevel@tonic-gate 			break;
1660Sstevel@tonic-gate 		case 4:
1670Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u32;
1680Sstevel@tonic-gate 			break;
1690Sstevel@tonic-gate 		case 2:
1700Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u16;
1710Sstevel@tonic-gate 			break;
1720Sstevel@tonic-gate 		case 1:
1730Sstevel@tonic-gate 			*value_p = (uint64_t)peek_value.u8;
1740Sstevel@tonic-gate 			break;
1750Sstevel@tonic-gate 		default:
1760Sstevel@tonic-gate 			err = DDI_FAILURE;
1770Sstevel@tonic-gate 		}
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	return (err);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * Safe C wrapper around assy language routine pci_do_phys_poke
1850Sstevel@tonic-gate  *
1860Sstevel@tonic-gate  * Type is TRUE for big endian, FALSE for little endian.
1870Sstevel@tonic-gate  * Size is 1,2,4 or 8 bytes.
1880Sstevel@tonic-gate  * paddr is the physical address in IO space to access read.
1890Sstevel@tonic-gate  * value contains the value to be written.
1900Sstevel@tonic-gate  */
1910Sstevel@tonic-gate static int
pcitool_phys_poke(pci_t * pci_p,boolean_t type,size_t size,uint64_t paddr,uint64_t value)192117Sschwartz pcitool_phys_poke(pci_t *pci_p, boolean_t type, size_t size,
193117Sschwartz 	uint64_t paddr, uint64_t value)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	on_trap_data_t otd;
1960Sstevel@tonic-gate 	int err = DDI_SUCCESS;
1970Sstevel@tonic-gate 	peek_poke_value_t poke_value;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	pbm_t *pbm_p = pci_p->pci_pbm_p;
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	switch (size) {
2020Sstevel@tonic-gate 	case 8:
2030Sstevel@tonic-gate 		poke_value.u64 = value;
2040Sstevel@tonic-gate 		break;
2050Sstevel@tonic-gate 	case 4:
2060Sstevel@tonic-gate 		poke_value.u32 = (uint32_t)value;
2070Sstevel@tonic-gate 		break;
2080Sstevel@tonic-gate 	case 2:
2090Sstevel@tonic-gate 		poke_value.u16 = (uint16_t)value;
2100Sstevel@tonic-gate 		break;
2110Sstevel@tonic-gate 	case 1:
2120Sstevel@tonic-gate 		poke_value.u8 = (uint8_t)value;
2130Sstevel@tonic-gate 		break;
2140Sstevel@tonic-gate 	default:
2150Sstevel@tonic-gate 		return (DDI_FAILURE);
2160Sstevel@tonic-gate 	}
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	mutex_enter(&pbm_p->pbm_pokefault_mutex);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = &otd;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	/*
2230Sstevel@tonic-gate 	 * on_trap works like setjmp.
2240Sstevel@tonic-gate 	 * Set it up to not panic on data access error,
2250Sstevel@tonic-gate 	 * but to call poke_fault instead.
2260Sstevel@tonic-gate 	 * Call pci_do_phys_poke after trap handling is setup.
2270Sstevel@tonic-gate 	 * When on_trap returns FALSE, it has been setup.
2280Sstevel@tonic-gate 	 * When it returns TRUE, an it has caught an error.
2290Sstevel@tonic-gate 	 */
2300Sstevel@tonic-gate 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
2310Sstevel@tonic-gate 		otd.ot_trampoline = (uintptr_t)&poke_fault;
2320Sstevel@tonic-gate 		err = pci_do_phys_poke(size, paddr, &poke_value.u64, type);
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/* Let the dust settle and errors occur if they will. */
2360Sstevel@tonic-gate 	pbm_clear_error(pbm_p);
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	/* Check for an error. */
2390Sstevel@tonic-gate 	if (otd.ot_trap == OT_DATA_ACCESS) {
2400Sstevel@tonic-gate 		err = DDI_FAILURE;
2410Sstevel@tonic-gate 	}
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	pbm_p->pbm_ontrap_data = NULL;
2440Sstevel@tonic-gate 	mutex_exit(&pbm_p->pbm_pokefault_mutex);
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	no_trap();
2470Sstevel@tonic-gate 	return (err);
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 
2514397Sschwartz /*ARGSUSED*/
2520Sstevel@tonic-gate static int
pcitool_intr_info(dev_info_t * dip,void * arg,int mode)2534397Sschwartz pcitool_intr_info(dev_info_t *dip, void *arg, int mode)
2540Sstevel@tonic-gate {
2554397Sschwartz 	pcitool_intr_info_t intr_info;
2564397Sschwartz 	int rval = SUCCESS;
2570Sstevel@tonic-gate 
2584397Sschwartz 	/* If we need user_version, and to ret same user version as passed in */
2594397Sschwartz 	if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
2600Sstevel@tonic-gate 	    DDI_SUCCESS) {
2610Sstevel@tonic-gate 		return (EFAULT);
2620Sstevel@tonic-gate 	}
2634397Sschwartz 
264*10053SEvan.Yan@Sun.COM 	if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
265*10053SEvan.Yan@Sun.COM 		return (ENOTSUP);
266*10053SEvan.Yan@Sun.COM 
2674397Sschwartz 	intr_info.ctlr_version = 0;	/* XXX how to get real version? */
2684397Sschwartz 	intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
2694397Sschwartz 	intr_info.num_intr = PCI_MAX_INO;
2704397Sschwartz 
2714397Sschwartz 	intr_info.drvr_version = PCITOOL_VERSION;
2724397Sschwartz 	if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
2734397Sschwartz 	    DDI_SUCCESS) {
2744397Sschwartz 		rval = EFAULT;
2754397Sschwartz 	}
2764397Sschwartz 
2774397Sschwartz 	return (rval);
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate  * Get interrupt information for a given ino.
2830Sstevel@tonic-gate  * Returns info only for inos mapped to devices.
2840Sstevel@tonic-gate  *
2850Sstevel@tonic-gate  * Returned info is valid only when iget.num_devs is returned > 0.
2860Sstevel@tonic-gate  * If ino is not enabled or is not mapped to a device, num_devs will be = 0.
2870Sstevel@tonic-gate  */
2880Sstevel@tonic-gate /*ARGSUSED*/
2890Sstevel@tonic-gate static int
pcitool_get_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)2900Sstevel@tonic-gate pcitool_get_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
2910Sstevel@tonic-gate {
2920Sstevel@tonic-gate 	/* Array part isn't used here, but oh well... */
2930Sstevel@tonic-gate 	pcitool_intr_get_t partial_iget;
2940Sstevel@tonic-gate 	pcitool_intr_get_t *iget = &partial_iget;
2950Sstevel@tonic-gate 	size_t	iget_kmem_alloc_size = 0;
2960Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
2970Sstevel@tonic-gate 	volatile uint64_t *imregp;
2980Sstevel@tonic-gate 	uint64_t imregval;
2990Sstevel@tonic-gate 	uint32_t ino;
3000Sstevel@tonic-gate 	uint8_t num_devs_ret;
301*10053SEvan.Yan@Sun.COM 	int cpu_id;
3020Sstevel@tonic-gate 	int copyout_rval;
3030Sstevel@tonic-gate 	int rval = SUCCESS;
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	/* Read in just the header part, no array section. */
3060Sstevel@tonic-gate 	if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
3070Sstevel@tonic-gate 	    DDI_SUCCESS) {
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 		return (EFAULT);
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
312*10053SEvan.Yan@Sun.COM 	if (partial_iget.flags & PCITOOL_INTR_FLAG_GET_MSI) {
313*10053SEvan.Yan@Sun.COM 		partial_iget.status = PCITOOL_IO_ERROR;
314*10053SEvan.Yan@Sun.COM 		partial_iget.num_devs_ret = 0;
315*10053SEvan.Yan@Sun.COM 		rval = ENOTSUP;
316*10053SEvan.Yan@Sun.COM 		goto done_get_intr;
317*10053SEvan.Yan@Sun.COM 	}
318*10053SEvan.Yan@Sun.COM 
3190Sstevel@tonic-gate 	ino = partial_iget.ino;
3200Sstevel@tonic-gate 	num_devs_ret = partial_iget.num_devs_ret;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	/* Validate argument. */
3230Sstevel@tonic-gate 	if (ino > PCI_MAX_INO) {
3240Sstevel@tonic-gate 		partial_iget.status = PCITOOL_INVALID_INO;
3250Sstevel@tonic-gate 		partial_iget.num_devs_ret = 0;
3260Sstevel@tonic-gate 		rval = EINVAL;
3270Sstevel@tonic-gate 		goto done_get_intr;
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/* Caller wants device information returned. */
3310Sstevel@tonic-gate 	if (num_devs_ret > 0) {
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 		/*
3340Sstevel@tonic-gate 		 * Allocate room.
3350Sstevel@tonic-gate 		 * Note if num_devs_ret == 0 iget remains pointing to
3360Sstevel@tonic-gate 		 * partial_iget.
3370Sstevel@tonic-gate 		 */
3380Sstevel@tonic-gate 		iget_kmem_alloc_size = PCITOOL_IGET_SIZE(num_devs_ret);
3390Sstevel@tonic-gate 		iget = kmem_alloc(iget_kmem_alloc_size, KM_SLEEP);
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 		/* Read in whole structure to verify there's room. */
3420Sstevel@tonic-gate 		if (ddi_copyin(arg, iget, iget_kmem_alloc_size, mode) !=
3430Sstevel@tonic-gate 		    SUCCESS) {
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 			/* Be consistent and just return EFAULT here. */
3460Sstevel@tonic-gate 			kmem_free(iget, iget_kmem_alloc_size);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 			return (EFAULT);
3490Sstevel@tonic-gate 		}
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	bzero(iget, PCITOOL_IGET_SIZE(num_devs_ret));
3530Sstevel@tonic-gate 	iget->ino = ino;
3540Sstevel@tonic-gate 	iget->num_devs_ret = num_devs_ret;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	imregp = ib_intr_map_reg_addr(ib_p, ino);
3570Sstevel@tonic-gate 	imregval = *imregp;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	/*
3600Sstevel@tonic-gate 	 * Read "valid" bit.  If set, interrupts are enabled.
3610Sstevel@tonic-gate 	 * This bit happens to be the same on Fire and Tomatillo.
3620Sstevel@tonic-gate 	 */
3630Sstevel@tonic-gate 	if (imregval & COMMON_INTR_MAP_REG_VALID) {
3640Sstevel@tonic-gate 		/*
3650Sstevel@tonic-gate 		 * The following looks up the ib_ino_info and returns
3660Sstevel@tonic-gate 		 * info of devices mapped to this ino.
3670Sstevel@tonic-gate 		 */
3680Sstevel@tonic-gate 		iget->num_devs = ib_get_ino_devs(
3690Sstevel@tonic-gate 		    ib_p, ino, &iget->num_devs_ret, iget->dev);
3700Sstevel@tonic-gate 
371*10053SEvan.Yan@Sun.COM 		if (ib_get_intr_target(pci_p, ino, &cpu_id) != DDI_SUCCESS) {
372*10053SEvan.Yan@Sun.COM 			iget->status = PCITOOL_IO_ERROR;
373*10053SEvan.Yan@Sun.COM 			rval = EIO;
374*10053SEvan.Yan@Sun.COM 			goto done_get_intr;
375*10053SEvan.Yan@Sun.COM 		}
376*10053SEvan.Yan@Sun.COM 
3770Sstevel@tonic-gate 		/*
3780Sstevel@tonic-gate 		 * Consider only inos mapped to devices (as opposed to
3790Sstevel@tonic-gate 		 * inos mapped to the bridge itself.
3800Sstevel@tonic-gate 		 */
3810Sstevel@tonic-gate 		if (iget->num_devs > 0) {
3820Sstevel@tonic-gate 			/*
3830Sstevel@tonic-gate 			 * These 2 items are platform specific,
3840Sstevel@tonic-gate 			 * extracted from the bridge.
3850Sstevel@tonic-gate 			 */
3860Sstevel@tonic-gate 			iget->ctlr = 0;
387*10053SEvan.Yan@Sun.COM 			iget->cpu_id = cpu_id;
3880Sstevel@tonic-gate 		}
3890Sstevel@tonic-gate 	}
3900Sstevel@tonic-gate done_get_intr:
3914397Sschwartz 	iget->drvr_version = PCITOOL_VERSION;
3920Sstevel@tonic-gate 	copyout_rval = ddi_copyout(iget, arg,
3930Sstevel@tonic-gate 	    PCITOOL_IGET_SIZE(num_devs_ret), mode);
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 	if (iget_kmem_alloc_size > 0) {
3960Sstevel@tonic-gate 		kmem_free(iget, iget_kmem_alloc_size);
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	if (copyout_rval != DDI_SUCCESS) {
4000Sstevel@tonic-gate 		rval = EFAULT;
4010Sstevel@tonic-gate 	}
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	return (rval);
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate /*
4070Sstevel@tonic-gate  * Associate a new CPU with a given ino.
4080Sstevel@tonic-gate  *
4090Sstevel@tonic-gate  * Operate only on inos which are already mapped to devices.
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate static int
pcitool_set_intr(dev_info_t * dip,void * arg,int mode,pci_t * pci_p)4120Sstevel@tonic-gate pcitool_set_intr(dev_info_t *dip, void *arg, int mode, pci_t *pci_p)
4130Sstevel@tonic-gate {
414117Sschwartz 	ib_t *ib_p = pci_p->pci_ib_p;
415117Sschwartz 	int rval = SUCCESS;
416*10053SEvan.Yan@Sun.COM 	int ret = DDI_SUCCESS;
4170Sstevel@tonic-gate 	uint8_t zero = 0;
4180Sstevel@tonic-gate 	pcitool_intr_set_t iset;
419*10053SEvan.Yan@Sun.COM 	volatile uint64_t *imregp;
4200Sstevel@tonic-gate 	uint64_t imregval;
421*10053SEvan.Yan@Sun.COM 
4224397Sschwartz 	size_t copyinout_size;
423*10053SEvan.Yan@Sun.COM 	int old_cpu_id;
4240Sstevel@tonic-gate 
4254397Sschwartz 	bzero(&iset, sizeof (pcitool_intr_set_t));
4264397Sschwartz 
4274397Sschwartz 	/* Version 1 of pcitool_intr_set_t doesn't have flags. */
4284397Sschwartz 	copyinout_size = (size_t)&iset.flags - (size_t)&iset;
4294397Sschwartz 
4304397Sschwartz 	if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4310Sstevel@tonic-gate 		return (EFAULT);
4320Sstevel@tonic-gate 
4334397Sschwartz 	switch (iset.user_version) {
4344397Sschwartz 	case PCITOOL_V1:
4354397Sschwartz 		break;
4364397Sschwartz 
4374397Sschwartz 	case PCITOOL_V2:
4384397Sschwartz 		copyinout_size = sizeof (pcitool_intr_set_t);
4394397Sschwartz 		if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
4404397Sschwartz 			return (EFAULT);
4414397Sschwartz 		break;
4424397Sschwartz 
4434397Sschwartz 	default:
4444397Sschwartz 		iset.status = PCITOOL_OUT_OF_RANGE;
4454397Sschwartz 		rval = ENOTSUP;
4464397Sschwartz 		goto done_set_intr;
4474397Sschwartz 	}
4484397Sschwartz 
449*10053SEvan.Yan@Sun.COM 	if ((iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) ||
450*10053SEvan.Yan@Sun.COM 	    (iset.flags & PCITOOL_INTR_FLAG_SET_MSI)) {
4514397Sschwartz 		iset.status = PCITOOL_IO_ERROR;
4524397Sschwartz 		rval = ENOTSUP;
4534397Sschwartz 		goto done_set_intr;
4544397Sschwartz 	}
4554397Sschwartz 
456117Sschwartz 	/* Validate input argument and that ino given belongs to a device. */
457117Sschwartz 	if ((iset.ino > PCI_MAX_INO) ||
458117Sschwartz 	    (ib_get_ino_devs(ib_p, iset.ino, &zero, NULL) == 0)) {
4590Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4600Sstevel@tonic-gate 		rval = EINVAL;
4610Sstevel@tonic-gate 		goto done_set_intr;
4620Sstevel@tonic-gate 	}
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	imregp = (uint64_t *)ib_intr_map_reg_addr(ib_p, iset.ino);
4650Sstevel@tonic-gate 	imregval = *imregp;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	/* Operate only on inos which are already enabled. */
4680Sstevel@tonic-gate 	if (!(imregval & COMMON_INTR_MAP_REG_VALID)) {
4690Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_INO;
4700Sstevel@tonic-gate 		rval = EINVAL;
4710Sstevel@tonic-gate 		goto done_set_intr;
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
474*10053SEvan.Yan@Sun.COM 	if (ib_get_intr_target(pci_p, iset.ino, &old_cpu_id) != DDI_SUCCESS) {
475*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_INVALID_INO;
476*10053SEvan.Yan@Sun.COM 		rval = EINVAL;
477*10053SEvan.Yan@Sun.COM 		goto done_set_intr;
478*10053SEvan.Yan@Sun.COM 	}
4790Sstevel@tonic-gate 
480*10053SEvan.Yan@Sun.COM 	if ((ret = ib_set_intr_target(pci_p, iset.ino,
481*10053SEvan.Yan@Sun.COM 	    iset.cpu_id)) == DDI_SUCCESS) {
482*10053SEvan.Yan@Sun.COM 		iset.cpu_id = old_cpu_id;
483*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_SUCCESS;
484*10053SEvan.Yan@Sun.COM 		goto done_set_intr;
4850Sstevel@tonic-gate 	}
4860Sstevel@tonic-gate 
487*10053SEvan.Yan@Sun.COM 	switch (ret) {
488*10053SEvan.Yan@Sun.COM 	case DDI_EPENDING:
489*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_PENDING_INTRTIMEOUT;
490*10053SEvan.Yan@Sun.COM 		rval = ETIME;
491*10053SEvan.Yan@Sun.COM 		break;
492*10053SEvan.Yan@Sun.COM 	case DDI_EINVAL:
4930Sstevel@tonic-gate 		iset.status = PCITOOL_INVALID_CPUID;
4940Sstevel@tonic-gate 		rval = EINVAL;
495*10053SEvan.Yan@Sun.COM 		break;
496*10053SEvan.Yan@Sun.COM 	default:
497*10053SEvan.Yan@Sun.COM 		iset.status = PCITOOL_INVALID_INO;
498*10053SEvan.Yan@Sun.COM 		rval = EINVAL;
499*10053SEvan.Yan@Sun.COM 		break;
5000Sstevel@tonic-gate 	}
5010Sstevel@tonic-gate done_set_intr:
5024397Sschwartz 	iset.drvr_version = PCITOOL_VERSION;
5034397Sschwartz 	if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
5040Sstevel@tonic-gate 		rval = EFAULT;
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	return (rval);
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate /* Main function for handling interrupt CPU binding requests and queries. */
5110Sstevel@tonic-gate int
pcitool_intr_admn(dev_t dev,void * arg,int cmd,int mode)5120Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
5130Sstevel@tonic-gate {
5140Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
5150Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
5160Sstevel@tonic-gate 	int		rval = SUCCESS;
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	switch (cmd) {
5190Sstevel@tonic-gate 
5204397Sschwartz 	/* Get system interrupt information. */
5214397Sschwartz 	case PCITOOL_SYSTEM_INTR_INFO:
5224397Sschwartz 		rval = pcitool_intr_info(dip, arg, mode);
5230Sstevel@tonic-gate 		break;
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	/* Get interrupt information for a given ino. */
5260Sstevel@tonic-gate 	case PCITOOL_DEVICE_GET_INTR:
5270Sstevel@tonic-gate 		rval = pcitool_get_intr(dip, arg, mode, pci_p);
5280Sstevel@tonic-gate 		break;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	/* Associate a new CPU with a given ino. */
5310Sstevel@tonic-gate 	case PCITOOL_DEVICE_SET_INTR:
5320Sstevel@tonic-gate 		rval = pcitool_set_intr(dip, arg, mode, pci_p);
5330Sstevel@tonic-gate 		break;
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	default:
5360Sstevel@tonic-gate 		rval = ENOTTY;
5370Sstevel@tonic-gate 	}
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	return (rval);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate /*
544117Sschwartz  * Wrapper around pcitool_phys_peek/poke.
5450Sstevel@tonic-gate  *
546117Sschwartz  * Validates arguments and calls pcitool_phys_peek/poke appropriately.
5470Sstevel@tonic-gate  *
5480Sstevel@tonic-gate  * Dip is of the nexus,
5490Sstevel@tonic-gate  * phys_addr is the address to write in physical space,
5500Sstevel@tonic-gate  * max_addr is the upper bound on the physical space used for bounds checking,
5510Sstevel@tonic-gate  * pcitool_status returns more detailed status in addition to a more generic
5520Sstevel@tonic-gate  * errno-style function return value.
5530Sstevel@tonic-gate  * other args are self-explanatory.
5540Sstevel@tonic-gate  */
5550Sstevel@tonic-gate static int
pcitool_access(pci_t * pci_p,uint64_t phys_addr,uint64_t max_addr,uint64_t * data,uint8_t size,boolean_t write,boolean_t endian,uint32_t * pcitool_status)556117Sschwartz pcitool_access(pci_t *pci_p, uint64_t phys_addr, uint64_t max_addr,
557117Sschwartz 	uint64_t *data, uint8_t size, boolean_t write, boolean_t endian,
558117Sschwartz 	uint32_t *pcitool_status)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 	int rval = SUCCESS;
5620Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	/* Upper bounds checking. */
5650Sstevel@tonic-gate 	if (phys_addr > max_addr) {
5660Sstevel@tonic-gate 		DEBUG2(DBG_TOOLS, dip,
5670Sstevel@tonic-gate 		    "Phys addr 0x%llx out of range (max 0x%llx).\n",
5680Sstevel@tonic-gate 		    phys_addr, max_addr);
5690Sstevel@tonic-gate 		*pcitool_status = PCITOOL_INVALID_ADDRESS;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 		rval = EINVAL;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	/* Alignment checking. */
5740Sstevel@tonic-gate 	} else if (!IS_P2ALIGNED(phys_addr, size)) {
5750Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "not aligned.\n");
5760Sstevel@tonic-gate 		*pcitool_status = PCITOOL_NOT_ALIGNED;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 		rval = EINVAL;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	/* Made it through checks.  Do the access. */
5810Sstevel@tonic-gate 	} else if (write) {
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
584117Sschwartz 		    "%d byte %s pcitool_phys_poke at addr 0x%llx\n",
5850Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
5860Sstevel@tonic-gate 
587117Sschwartz 		if (pcitool_phys_poke(pci_p, endian, size, phys_addr,
588117Sschwartz 		    *data) != DDI_SUCCESS) {
5890Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
590117Sschwartz 			    "%d byte %s pcitool_phys_poke at addr "
5910Sstevel@tonic-gate 			    "0x%llx failed\n",
5920Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
5930Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 			rval = EFAULT;
5960Sstevel@tonic-gate 		}
5970Sstevel@tonic-gate 
598117Sschwartz 	} else {	/* Read */
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 		DEBUG3(DBG_PHYS_ACC, dip,
601117Sschwartz 		    "%d byte %s pcitool_phys_peek at addr 0x%llx\n",
6020Sstevel@tonic-gate 		    size, (endian ? "BE" : "LE"), phys_addr);
6030Sstevel@tonic-gate 
604117Sschwartz 		if (pcitool_phys_peek(pci_p, endian, size, phys_addr,
605117Sschwartz 		    data) != DDI_SUCCESS) {
6060Sstevel@tonic-gate 			DEBUG3(DBG_PHYS_ACC, dip,
607117Sschwartz 			    "%d byte %s pcitool_phys_peek at addr "
6080Sstevel@tonic-gate 			    "0x%llx failed\n",
6090Sstevel@tonic-gate 			    size, (endian ? "BE" : "LE"), phys_addr);
6100Sstevel@tonic-gate 			*pcitool_status = PCITOOL_INVALID_ADDRESS;
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 			rval = EFAULT;
6130Sstevel@tonic-gate 		}
6140Sstevel@tonic-gate 	}
6150Sstevel@tonic-gate 	return (rval);
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate  * Perform register accesses on the nexus device itself.
6200Sstevel@tonic-gate  */
6210Sstevel@tonic-gate int
pcitool_bus_reg_ops(dev_t dev,void * arg,int cmd,int mode)6220Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
6230Sstevel@tonic-gate {
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	pci_t			*pci_p = DEV_TO_SOFTSTATE(dev);
6260Sstevel@tonic-gate 	dev_info_t		*dip = pci_p->pci_dip;
627117Sschwartz 	pci_nexus_regspec_t	*pci_rp = NULL;
628117Sschwartz 	boolean_t		write_flag = B_FALSE;
6290Sstevel@tonic-gate 	pcitool_reg_t		prg;
6300Sstevel@tonic-gate 	uint64_t		base_addr;
6310Sstevel@tonic-gate 	uint64_t		max_addr;
6320Sstevel@tonic-gate 	uint32_t		reglen;
6330Sstevel@tonic-gate 	uint8_t			size;
6340Sstevel@tonic-gate 	uint32_t		rval = 0;
6350Sstevel@tonic-gate 
636117Sschwartz 	if (cmd == PCITOOL_NEXUS_SET_REG)
6370Sstevel@tonic-gate 		write_flag = B_TRUE;
6380Sstevel@tonic-gate 
639117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_bus_reg_ops nexus set/get reg\n");
6400Sstevel@tonic-gate 
641117Sschwartz 	/* Read data from userland. */
642117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
643117Sschwartz 	    DDI_SUCCESS) {
644117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
645117Sschwartz 		return (EFAULT);
646117Sschwartz 	}
6470Sstevel@tonic-gate 
648117Sschwartz 	/* Read reg property which contains starting addr and size of banks. */
649117Sschwartz 	if (ddi_prop_lookup_int_array(
650117Sschwartz 	    DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
651117Sschwartz 	    "reg", (int **)&pci_rp, &reglen) == DDI_SUCCESS) {
652117Sschwartz 		if (((reglen * sizeof (int)) %
653117Sschwartz 		    sizeof (pci_nexus_regspec_t)) != 0) {
654117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "reg prop not well-formed");
655117Sschwartz 			prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
656117Sschwartz 			rval = EIO;
6570Sstevel@tonic-gate 			goto done;
6580Sstevel@tonic-gate 		}
659117Sschwartz 	}
6600Sstevel@tonic-gate 
661117Sschwartz 	/* Bounds check the bank number. */
662117Sschwartz 	if (prg.barnum >=
663117Sschwartz 	    (reglen * sizeof (int)) / sizeof (pci_nexus_regspec_t)) {
664117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
665117Sschwartz 		rval = EINVAL;
666117Sschwartz 		goto done;
6670Sstevel@tonic-gate 	}
6680Sstevel@tonic-gate 
669117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
670117Sschwartz 	base_addr = pci_rp[prg.barnum].phys_addr;
671117Sschwartz 	max_addr = base_addr + pci_rp[prg.barnum].size;
672117Sschwartz 	prg.phys_addr = base_addr + prg.offset;
673117Sschwartz 
674117Sschwartz 	DEBUG4(DBG_TOOLS, dip,
675117Sschwartz 	    "pcitool_bus_reg_ops: nexus: base:0x%llx, offset:0x%llx, "
676117Sschwartz 	    "addr:0x%llx, max_addr:0x%llx\n",
677117Sschwartz 	    base_addr, prg.offset, prg.phys_addr, max_addr);
678117Sschwartz 
679117Sschwartz 	/* Access device.  prg.status is modified. */
680117Sschwartz 	rval = pcitool_access(pci_p,
681117Sschwartz 	    prg.phys_addr, max_addr, &prg.data, size, write_flag,
682117Sschwartz 	    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
683117Sschwartz 
6840Sstevel@tonic-gate done:
685117Sschwartz 	if (pci_rp != NULL)
686117Sschwartz 		ddi_prop_free(pci_rp);
687117Sschwartz 
6884397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
6890Sstevel@tonic-gate 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
6900Sstevel@tonic-gate 	    DDI_SUCCESS) {
6910Sstevel@tonic-gate 		DEBUG0(DBG_TOOLS, dip, "Copyout failed.\n");
6920Sstevel@tonic-gate 		return (EFAULT);
6930Sstevel@tonic-gate 	}
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	return (rval);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 
699117Sschwartz static int
pcitool_validate_barnum_bdf(pcitool_reg_t * prg)700117Sschwartz pcitool_validate_barnum_bdf(pcitool_reg_t *prg)
701117Sschwartz {
702117Sschwartz 	int rval = SUCCESS;
703117Sschwartz 
704117Sschwartz 	if (prg->barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
705117Sschwartz 		prg->status = PCITOOL_OUT_OF_RANGE;
706117Sschwartz 		rval = EINVAL;
707117Sschwartz 
708117Sschwartz 	/* Validate address arguments of bus / dev / func */
709117Sschwartz 	} else if (((prg->bus_no &
710117Sschwartz 	    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) != prg->bus_no) ||
711117Sschwartz 	    ((prg->dev_no &
712117Sschwartz 	    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) != prg->dev_no) ||
713117Sschwartz 	    ((prg->func_no &
714117Sschwartz 	    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) != prg->func_no)) {
715117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
716117Sschwartz 		rval = EINVAL;
717117Sschwartz 	}
718117Sschwartz 
719117Sschwartz 	return (rval);
720117Sschwartz }
721117Sschwartz 
722117Sschwartz static int
pcitool_get_bar(pci_t * pci_p,pcitool_reg_t * prg,uint64_t config_base_addr,uint64_t config_max_addr,uint64_t * bar,boolean_t * is_io_space)723117Sschwartz pcitool_get_bar(pci_t *pci_p, pcitool_reg_t *prg, uint64_t config_base_addr,
724117Sschwartz 	uint64_t config_max_addr, uint64_t *bar, boolean_t *is_io_space)
725117Sschwartz {
726117Sschwartz 
727117Sschwartz 	uint8_t bar_offset;
728117Sschwartz 	int rval;
729117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
730117Sschwartz 
731117Sschwartz 	*bar = 0;
732117Sschwartz 	*is_io_space = B_FALSE;
733117Sschwartz 
734117Sschwartz 	/*
735117Sschwartz 	 * Translate BAR number into offset of the BAR in
736117Sschwartz 	 * the device's config space.
737117Sschwartz 	 */
738117Sschwartz 	bar_offset = PCI_BAR_OFFSET((*prg));
739117Sschwartz 
740117Sschwartz 	DEBUG2(DBG_TOOLS, dip, "barnum:%d, bar_offset:0x%x\n",
741117Sschwartz 	    prg->barnum, bar_offset);
742117Sschwartz 
743117Sschwartz 	/*
744117Sschwartz 	 * Get Bus Address Register (BAR) from config space.
745117Sschwartz 	 * bar_offset is the offset into config space of the BAR desired.
746117Sschwartz 	 * prg->status is modified on error.
747117Sschwartz 	 */
748117Sschwartz 	rval = pcitool_access(pci_p, config_base_addr + bar_offset,
749117Sschwartz 	    config_max_addr, bar,
750117Sschwartz 	    4,		/* 4 bytes. */
751117Sschwartz 	    B_FALSE,	/* Read */
752117Sschwartz 	    B_FALSE, 	/* Little endian. */
753117Sschwartz 	    &prg->status);
754117Sschwartz 	if (rval != SUCCESS)
755117Sschwartz 		return (rval);
756117Sschwartz 
757117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "bar returned is 0x%llx\n", *bar);
758117Sschwartz 	if (!(*bar)) {
759117Sschwartz 		prg->status = PCITOOL_INVALID_ADDRESS;
760117Sschwartz 		return (EINVAL);
761117Sschwartz 	}
762117Sschwartz 
763117Sschwartz 	/*
764117Sschwartz 	 * BAR has bits saying this space is IO space, unless
765117Sschwartz 	 * this is the ROM address register.
766117Sschwartz 	 */
767117Sschwartz 	if (((PCI_BASE_SPACE_M & *bar) == PCI_BASE_SPACE_IO) &&
768117Sschwartz 	    (bar_offset != PCI_CONF_ROM)) {
769117Sschwartz 		*is_io_space = B_TRUE;
770117Sschwartz 		*bar &= PCI_BASE_IO_ADDR_M;
771117Sschwartz 
772117Sschwartz 	/*
773117Sschwartz 	 * BAR has bits saying this space is 64 bit memory
774117Sschwartz 	 * space, unless this is the ROM address register.
775117Sschwartz 	 *
776117Sschwartz 	 * The 64 bit address stored in two BAR cells is not necessarily
777117Sschwartz 	 * aligned on an 8-byte boundary.  Need to keep the first 4
778117Sschwartz 	 * bytes read, and do a separate read of the high 4 bytes.
779117Sschwartz 	 */
780117Sschwartz 
781117Sschwartz 	} else if ((PCI_BASE_TYPE_ALL & *bar) && (bar_offset != PCI_CONF_ROM)) {
782117Sschwartz 
783117Sschwartz 		uint32_t low_bytes = (uint32_t)(*bar & ~PCI_BASE_TYPE_ALL);
784117Sschwartz 
785117Sschwartz 		/* Don't try to read past the end of BARs. */
786117Sschwartz 		if (bar_offset >= PCI_CONF_BASE5) {
787117Sschwartz 			prg->status = PCITOOL_OUT_OF_RANGE;
788117Sschwartz 			return (EIO);
789117Sschwartz 		}
790117Sschwartz 
791117Sschwartz 		/* Access device.  prg->status is modified on error. */
792117Sschwartz 		rval = pcitool_access(pci_p,
793117Sschwartz 		    config_base_addr + bar_offset + 4, config_max_addr, bar,
794117Sschwartz 		    4,		/* 4 bytes. */
795117Sschwartz 		    B_FALSE,	/* Read */
796117Sschwartz 		    B_FALSE, 	/* Little endian. */
797117Sschwartz 		    &prg->status);
798117Sschwartz 		if (rval != SUCCESS)
799117Sschwartz 			return (rval);
800117Sschwartz 
801117Sschwartz 		*bar = (*bar << 32) + low_bytes;
802117Sschwartz 	}
803117Sschwartz 
804117Sschwartz 	return (SUCCESS);
805117Sschwartz }
806117Sschwartz 
807117Sschwartz 
808117Sschwartz static int
pcitool_config_request(pci_t * pci_p,pcitool_reg_t * prg,uint64_t base_addr,uint64_t max_addr,uint8_t size,boolean_t write_flag)809117Sschwartz pcitool_config_request(pci_t *pci_p, pcitool_reg_t *prg, uint64_t base_addr,
810117Sschwartz 	uint64_t max_addr, uint8_t size, boolean_t write_flag)
811117Sschwartz {
812117Sschwartz 	int rval;
813117Sschwartz 	dev_info_t *dip = pci_p->pci_dip;
814117Sschwartz 
815117Sschwartz 	/* Access config space and we're done. */
816117Sschwartz 	prg->phys_addr = base_addr + prg->offset;
817117Sschwartz 
818117Sschwartz 	DEBUG4(DBG_TOOLS, dip, "config access: base:0x%llx, "
819117Sschwartz 	    "offset:0x%llx, phys_addr:0x%llx, end:%s\n",
820117Sschwartz 	    base_addr, prg->offset, prg->phys_addr,
821117Sschwartz 	    (PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr)? "big" : "ltl"));
822117Sschwartz 
823117Sschwartz 	/* Access device.  pr.status is modified. */
824117Sschwartz 	rval = pcitool_access(pci_p, prg->phys_addr, max_addr, &prg->data, size,
825117Sschwartz 	    write_flag, PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr), &prg->status);
826117Sschwartz 
827117Sschwartz 	DEBUG1(DBG_TOOLS, dip, "config access: data:0x%llx\n", prg->data);
828117Sschwartz 
829117Sschwartz 	return (rval);
830117Sschwartz }
831117Sschwartz 
8320Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
8330Sstevel@tonic-gate int
pcitool_dev_reg_ops(dev_t dev,void * arg,int cmd,int mode)8340Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
8350Sstevel@tonic-gate {
8360Sstevel@tonic-gate 	pci_t		*pci_p = DEV_TO_SOFTSTATE(dev);
8370Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
8380Sstevel@tonic-gate 	pci_ranges_t	*rp = pci_p->pci_ranges;
8390Sstevel@tonic-gate 	pcitool_reg_t	prg;
8400Sstevel@tonic-gate 	uint64_t	max_addr;
8410Sstevel@tonic-gate 	uint64_t	base_addr;
8420Sstevel@tonic-gate 	uint64_t	range_prop;
8430Sstevel@tonic-gate 	uint64_t	range_prop_size;
8440Sstevel@tonic-gate 	uint64_t	bar = 0;
8450Sstevel@tonic-gate 	int		rval = 0;
8460Sstevel@tonic-gate 	boolean_t	write_flag = B_FALSE;
847117Sschwartz 	boolean_t	is_io_space = B_FALSE;
8480Sstevel@tonic-gate 	uint8_t		size;
8490Sstevel@tonic-gate 
850117Sschwartz 	if (cmd == PCITOOL_DEVICE_SET_REG)
8510Sstevel@tonic-gate 		write_flag = B_TRUE;
8520Sstevel@tonic-gate 
853117Sschwartz 	DEBUG0(DBG_TOOLS, dip, "pcitool_dev_reg_ops set/get reg\n");
854117Sschwartz 	if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
855117Sschwartz 	    DDI_SUCCESS) {
856117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error reading arguments\n");
857117Sschwartz 		return (EFAULT);
858117Sschwartz 	}
8590Sstevel@tonic-gate 
860117Sschwartz 	DEBUG3(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n",
861117Sschwartz 	    prg.bus_no, prg.dev_no, prg.func_no);
8620Sstevel@tonic-gate 
863117Sschwartz 	if ((rval = pcitool_validate_barnum_bdf(&prg)) != SUCCESS)
864117Sschwartz 		goto done_reg;
865117Sschwartz 
866117Sschwartz 	size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
8670Sstevel@tonic-gate 
868117Sschwartz 	/* Get config space first. */
869117Sschwartz 	range_prop = PCI_GET_RANGE_PROP(rp, PCI_CONFIG_RANGE_BANK);
870117Sschwartz 	range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp, PCI_CONFIG_RANGE_BANK);
871117Sschwartz 	max_addr = range_prop + range_prop_size;
8720Sstevel@tonic-gate 
873117Sschwartz 	/*
874117Sschwartz 	 * Build device address based on base addr from range prop, and bus,
875117Sschwartz 	 * dev and func values passed in.  This address is where config space
876117Sschwartz 	 * begins.
877117Sschwartz 	 */
878117Sschwartz 	base_addr = range_prop +
879117Sschwartz 	    (prg.bus_no << PCI_REG_BUS_SHIFT) +
880117Sschwartz 	    (prg.dev_no << PCI_REG_DEV_SHIFT) +
881117Sschwartz 	    (prg.func_no << PCI_REG_FUNC_SHIFT);
8820Sstevel@tonic-gate 
883117Sschwartz 	if ((base_addr < range_prop) || (base_addr >= max_addr)) {
884117Sschwartz 		prg.status = PCITOOL_OUT_OF_RANGE;
885117Sschwartz 		rval = EINVAL;
886117Sschwartz 		goto done_reg;
887117Sschwartz 	}
8880Sstevel@tonic-gate 
889117Sschwartz 	DEBUG5(DBG_TOOLS, dip, "range_prop:0x%llx, shifted: bus:0x%x, dev:0x%x "
890117Sschwartz 	    "func:0x%x, addr:0x%x\n", range_prop,
891117Sschwartz 	    prg.bus_no << PCI_REG_BUS_SHIFT, prg.dev_no << PCI_REG_DEV_SHIFT,
892117Sschwartz 	    prg.func_no << PCI_REG_FUNC_SHIFT, base_addr);
8930Sstevel@tonic-gate 
894117Sschwartz 	/* Proper config space desired. */
895117Sschwartz 	if (prg.barnum == 0) {
896117Sschwartz 
897117Sschwartz 		rval = pcitool_config_request(pci_p, &prg, base_addr, max_addr,
898117Sschwartz 		    size, write_flag);
8990Sstevel@tonic-gate 
900117Sschwartz 	} else {	/* IO / MEM / MEM64 space. */
9010Sstevel@tonic-gate 
902117Sschwartz 		if (pcitool_get_bar(pci_p, &prg, base_addr, max_addr, &bar,
903117Sschwartz 		    &is_io_space) != SUCCESS)
904117Sschwartz 			goto done_reg;
9050Sstevel@tonic-gate 
906117Sschwartz 		/* IO space. */
907117Sschwartz 		if (is_io_space) {
9080Sstevel@tonic-gate 
909117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "IO space\n");
9100Sstevel@tonic-gate 
911117Sschwartz 			/* Reposition to focus on IO space. */
912117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_IO_RANGE_BANK);
913117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
914117Sschwartz 			    PCI_IO_RANGE_BANK);
9150Sstevel@tonic-gate 
916117Sschwartz 		/* 64 bit memory space. */
917117Sschwartz 		} else if ((bar >> 32) != 0) {
918117Sschwartz 
919117Sschwartz 			DEBUG1(DBG_TOOLS, dip,
920117Sschwartz 			    "64 bit mem space.  64-bit bar is 0x%llx\n", bar);
9210Sstevel@tonic-gate 
922117Sschwartz 			/* Reposition to MEM64 range space. */
923117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp,
924117Sschwartz 			    PCI_MEM64_RANGE_BANK);
925117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
926117Sschwartz 			    PCI_MEM64_RANGE_BANK);
9270Sstevel@tonic-gate 
928117Sschwartz 		} else {	/* Mem32 space, including ROM */
9290Sstevel@tonic-gate 
930117Sschwartz 			DEBUG0(DBG_TOOLS, dip, "32 bit mem space\n");
9310Sstevel@tonic-gate 
932117Sschwartz 			if (PCI_BAR_OFFSET(prg) == PCI_CONF_ROM) {
9330Sstevel@tonic-gate 
934117Sschwartz 				DEBUG0(DBG_TOOLS, dip,
935117Sschwartz 				    "Additional ROM checking\n");
9360Sstevel@tonic-gate 
937117Sschwartz 				/* Can't write to ROM */
938117Sschwartz 				if (write_flag) {
939117Sschwartz 					prg.status = PCITOOL_ROM_WRITE;
940117Sschwartz 					rval = EIO;
941117Sschwartz 					goto done_reg;
9420Sstevel@tonic-gate 
943117Sschwartz 				/* ROM disabled for reading */
944117Sschwartz 				} else if (!(bar & 0x00000001)) {
945117Sschwartz 					prg.status = PCITOOL_ROM_DISABLED;
9460Sstevel@tonic-gate 					rval = EIO;
9470Sstevel@tonic-gate 					goto done_reg;
9480Sstevel@tonic-gate 				}
9490Sstevel@tonic-gate 			}
9500Sstevel@tonic-gate 
951117Sschwartz 			range_prop = PCI_GET_RANGE_PROP(rp, PCI_MEM_RANGE_BANK);
952117Sschwartz 			range_prop_size = PCI_GET_RANGE_PROP_SIZE(rp,
953117Sschwartz 			    PCI_MEM_RANGE_BANK);
954117Sschwartz 		}
9550Sstevel@tonic-gate 
956117Sschwartz 		/* Common code for all IO/MEM range spaces. */
957117Sschwartz 		max_addr = range_prop + range_prop_size;
958117Sschwartz 		base_addr = range_prop + bar;
959117Sschwartz 
960117Sschwartz 		DEBUG3(DBG_TOOLS, dip,
961117Sschwartz 		    "addr portion of bar is 0x%llx, base=0x%llx, "
962117Sschwartz 		    "offset:0x%lx\n", bar, base_addr, prg.offset);
9630Sstevel@tonic-gate 
964117Sschwartz 		/*
965117Sschwartz 		 * Use offset provided by caller to index into
966117Sschwartz 		 * desired space, then access.
967117Sschwartz 		 * Note that prg.status is modified on error.
968117Sschwartz 		 */
969117Sschwartz 		prg.phys_addr = base_addr + prg.offset;
970117Sschwartz 		rval = pcitool_access(pci_p, prg.phys_addr,
971117Sschwartz 		    max_addr, &prg.data, size, write_flag,
972117Sschwartz 		    PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), &prg.status);
973117Sschwartz 	}
974117Sschwartz 
9750Sstevel@tonic-gate done_reg:
9764397Sschwartz 	prg.drvr_version = PCITOOL_VERSION;
977117Sschwartz 	if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
978117Sschwartz 	    DDI_SUCCESS) {
979117Sschwartz 		DEBUG0(DBG_TOOLS, dip, "Error returning arguments.\n");
980117Sschwartz 		rval = EFAULT;
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 	return (rval);
9830Sstevel@tonic-gate }
984117Sschwartz 
985117Sschwartz int
pcitool_init(dev_info_t * dip)986117Sschwartz pcitool_init(dev_info_t *dip)
987117Sschwartz {
988117Sschwartz 	int instance = ddi_get_instance(dip);
989117Sschwartz 
990117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_REG, S_IFCHR,
991117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_REG_MINOR_NUM),
992117Sschwartz 	    DDI_NT_REGACC, 0) != DDI_SUCCESS)
993117Sschwartz 		return (DDI_FAILURE);
994117Sschwartz 
995117Sschwartz 	if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
996117Sschwartz 	    PCIHP_AP_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
997117Sschwartz 	    DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
998117Sschwartz 		ddi_remove_minor_node(dip, PCI_MINOR_REG);
999117Sschwartz 		return (DDI_FAILURE);
1000117Sschwartz 	}
1001117Sschwartz 
1002117Sschwartz 	return (DDI_SUCCESS);
1003117Sschwartz }
1004117Sschwartz 
1005117Sschwartz void
pcitool_uninit(dev_info_t * dip)1006117Sschwartz pcitool_uninit(dev_info_t *dip)
1007117Sschwartz {
1008117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_REG);
1009117Sschwartz 	ddi_remove_minor_node(dip, PCI_MINOR_INTR);
1010117Sschwartz }
1011