xref: /onnv-gate/usr/src/uts/common/io/pci_cap.c (revision 9284:f60784e63c92)
11624Spjha /*
21624Spjha  * CDDL HEADER START
31624Spjha  *
41624Spjha  * The contents of this file are subject to the terms of the
51624Spjha  * Common Development and Distribution License (the "License").
61624Spjha  * You may not use this file except in compliance with the License.
71624Spjha  *
81624Spjha  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91624Spjha  * or http://www.opensolaris.org/os/licensing.
101624Spjha  * See the License for the specific language governing permissions
111624Spjha  * and limitations under the License.
121624Spjha  *
131624Spjha  * When distributing Covered Code, include this CDDL HEADER in each
141624Spjha  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151624Spjha  * If applicable, add the following below this CDDL HEADER, with the
161624Spjha  * fields enclosed by brackets "[]" replaced with your own identifying
171624Spjha  * information: Portions Copyright [yyyy] [name of copyright owner]
181624Spjha  *
191624Spjha  * CDDL HEADER END
201624Spjha  */
211624Spjha 
221624Spjha #include <sys/note.h>
231624Spjha #include <sys/conf.h>
241624Spjha #include <sys/debug.h>
251624Spjha #include <sys/sunddi.h>
261624Spjha #include <sys/pci.h>
271624Spjha #include <sys/pcie.h>
281624Spjha #include <sys/bitmap.h>
291624Spjha #include <sys/autoconf.h>
301624Spjha #include <sys/sysmacros.h>
311624Spjha #include <sys/pci_cap.h>
321624Spjha 
331624Spjha /*
34*9284SCasper.Dik@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
351624Spjha  * Use is subject to license terms.
361624Spjha  */
371624Spjha 
381624Spjha /*
391624Spjha  * Generic PCI Capabilites Interface for all pci platforms
401624Spjha  */
411624Spjha 
421624Spjha #ifdef DEBUG
431624Spjha uint_t  pci_cap_debug = 0;
441624Spjha #endif
451624Spjha 
461624Spjha /* Cap Base Macro */
471624Spjha #define	PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
481624Spjha 	(id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
491624Spjha 
501624Spjha /*
511624Spjha  * pci_cap_probe: returns the capid and base based upon a given index
521624Spjha  */
531624Spjha int
541624Spjha pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
551624Spjha 	uint32_t *id_p, uint16_t *base_p)
561624Spjha {
571624Spjha 	int i, search_ext = 0;
581624Spjha 	uint16_t base, pcix_cmd, status;
591624Spjha 	uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
601624Spjha 
611624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
621624Spjha 
631774Spjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
641624Spjha 		return (DDI_FAILURE);
651624Spjha 
661624Spjha 	/* PCIE and PCIX Version 2 contain Extended Config Space */
671624Spjha 	for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
681624Spjha 		base && i < index; base = pci_config_get8(h, base
691624Spjha 			+ PCI_CAP_NEXT_PTR), i++) {
701624Spjha 
711624Spjha 		if ((id = pci_config_get8(h, base)) == 0xff)
721624Spjha 			break;
731624Spjha 
741624Spjha 		if (id == PCI_CAP_ID_PCI_E)
751624Spjha 			search_ext = 1;
761624Spjha 		else if (id == PCI_CAP_ID_PCIX) {
771624Spjha 			if ((pcix_cmd = pci_config_get16(h, base +
781774Spjha 				PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
791624Spjha 				continue;
801624Spjha 			if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
811624Spjha 				search_ext = 1;
821624Spjha 		}
831624Spjha 	}
841624Spjha 
851624Spjha 	if (base && i == index) {
861624Spjha 		if ((id = pci_config_get8(h, base)) != 0xff)
871624Spjha 			goto found;
881624Spjha 	}
891624Spjha 
901624Spjha 	if (!search_ext)
911624Spjha 		return (DDI_FAILURE);
921624Spjha 
931624Spjha 	for (base = PCIE_EXT_CAP; base && i < index; i++) {
941774Spjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
951624Spjha 			break;
961624Spjha 
971624Spjha 		id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
981624Spjha 			& PCIE_EXT_CAP_ID_MASK;
991624Spjha 		base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
1001624Spjha 			& PCIE_EXT_CAP_NEXT_PTR_MASK;
1011624Spjha 	}
1021624Spjha 
1031624Spjha 	if (!base || i < index)
1041624Spjha 		return (DDI_FAILURE);
1051624Spjha 
1061774Spjha 	if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
1071624Spjha 		return (DDI_FAILURE);
1081624Spjha 
1091624Spjha 	id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
1101624Spjha 			PCI_CAP_XCFG_FLAG;
1111624Spjha found:
1121624Spjha 	PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
1131624Spjha 		index, id, base);
1141624Spjha 
1151624Spjha 	*id_p = id;
1161624Spjha 	*base_p = base;
1171624Spjha 	return (DDI_SUCCESS);
1181624Spjha 
1191624Spjha }
1201624Spjha 
1211624Spjha /*
1221624Spjha  * pci_lcap_locate: Helper function locates a base in conventional config space.
1231624Spjha  */
1241624Spjha int
1251624Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
1261624Spjha {
127*9284SCasper.Dik@Sun.COM 	uint8_t header;
1281624Spjha 	uint16_t status, base;
1291624Spjha 
1301624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
1311624Spjha 
1321774Spjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
1331624Spjha 		return (DDI_FAILURE);
1341624Spjha 
135*9284SCasper.Dik@Sun.COM 	header = pci_config_get8(h, PCI_CONF_HEADER);
136*9284SCasper.Dik@Sun.COM 	switch (header & PCI_HEADER_TYPE_M) {
137*9284SCasper.Dik@Sun.COM 	case PCI_HEADER_ZERO:
138*9284SCasper.Dik@Sun.COM 		base = PCI_CONF_CAP_PTR;
139*9284SCasper.Dik@Sun.COM 		break;
140*9284SCasper.Dik@Sun.COM 	case PCI_HEADER_PPB:
141*9284SCasper.Dik@Sun.COM 		base = PCI_BCNF_CAP_PTR;
142*9284SCasper.Dik@Sun.COM 		break;
143*9284SCasper.Dik@Sun.COM 	case PCI_HEADER_CARDBUS:
144*9284SCasper.Dik@Sun.COM 		base = PCI_CBUS_CAP_PTR;
145*9284SCasper.Dik@Sun.COM 		break;
146*9284SCasper.Dik@Sun.COM 	default:
147*9284SCasper.Dik@Sun.COM 		cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
148*9284SCasper.Dik@Sun.COM 			__func__, header);
149*9284SCasper.Dik@Sun.COM 		return (DDI_FAILURE);
150*9284SCasper.Dik@Sun.COM 	}
151*9284SCasper.Dik@Sun.COM 
152*9284SCasper.Dik@Sun.COM 	for (base = pci_config_get8(h, base); base;
1531624Spjha 		base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
1541624Spjha 		if (pci_config_get8(h, base) == id) {
1551624Spjha 			*base_p = base;
1561624Spjha 			return (DDI_SUCCESS);
1571624Spjha 		}
1581624Spjha 	}
1591624Spjha 
1601624Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
1611624Spjha 	return (DDI_FAILURE);
1621624Spjha 
1631624Spjha 
1641624Spjha }
1651624Spjha 
1661624Spjha /*
1671624Spjha  * pci_xcap_locate: Helper function locates a base in extended config space.
1681624Spjha  */
1691624Spjha int
1701624Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
1711624Spjha {
1721624Spjha 	uint16_t status, base;
1731624Spjha 	uint32_t xcaps_hdr;
1741624Spjha 
1751624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
1761624Spjha 
1771774Spjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
1781624Spjha 		return (DDI_FAILURE);
1791624Spjha 
1801624Spjha 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
1811624Spjha 		PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
1821624Spjha 
1831774Spjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
1841624Spjha 			break;
1851624Spjha 
1861624Spjha 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
1871624Spjha 			PCIE_EXT_CAP_ID_MASK) == id) {
1881624Spjha 			*base_p = base;
1891624Spjha 			return (DDI_SUCCESS);
1901624Spjha 		}
1911624Spjha 	}
1921624Spjha 
1931624Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
1941624Spjha 	return (DDI_FAILURE);
1951624Spjha }
1961624Spjha 
1971624Spjha /*
1981624Spjha  * pci_cap_get: This function uses the base or capid to get a byte, word,
1991624Spjha  * or dword. If access by capid is requested, the function uses the capid to
2001624Spjha  * locate the base. Access by a base results in better performance
2011624Spjha  * because no cap list traversal is required.
2021624Spjha  */
2031624Spjha uint32_t
2042587Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
2051624Spjha 	uint32_t id, uint16_t base, uint16_t offset)
2061624Spjha {
2071624Spjha 	uint32_t data;
2081624Spjha 
2091624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2101774Spjha 		return (PCI_CAP_EINVAL32);
2111624Spjha 
2121624Spjha 	/*
2131644Spjha 	 * Each access to a PCI Configuration Space should be checked
2141644Spjha 	 * by the calling function. A returned value of the 2's complement
2151644Spjha 	 * of -1 indicates that either the device is offlined or it does not
2161644Spjha 	 * exist.
2171624Spjha 	 */
2181624Spjha 	offset += base;
2191624Spjha 
2201624Spjha 	switch (size) {
2211624Spjha 	case PCI_CAP_CFGSZ_8:
2221644Spjha 		data = pci_config_get8(h, offset);
2231624Spjha 		break;
2241624Spjha 	case PCI_CAP_CFGSZ_16:
2251644Spjha 		data = pci_config_get16(h, offset);
2261624Spjha 		break;
2271624Spjha 	case PCI_CAP_CFGSZ_32:
2281644Spjha 		data = pci_config_get32(h, offset);
2291624Spjha 		break;
2301624Spjha 	default:
2311774Spjha 		data = PCI_CAP_EINVAL32;
2321624Spjha 	}
2331624Spjha 
2341624Spjha 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
2351624Spjha 	return (data);
2361624Spjha }
2371624Spjha 
2381624Spjha /*
2391624Spjha  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
2401624Spjha  * or dword. If access by capid is requested, the function uses the capid to
2411624Spjha  * locate the base. Access by base results in better performance
2421624Spjha  * because no cap list traversal is required.
2431624Spjha  */
2441624Spjha int
2452587Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
2461624Spjha 	uint32_t id, uint16_t base, uint16_t offset,
2471624Spjha 	uint32_t data)
2481624Spjha {
2491624Spjha 
2501624Spjha 	/*
2511624Spjha 	 * use the pci_config_size_t to switch for the appropriate read
2521624Spjha 	 */
2531624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2541624Spjha 		return (DDI_FAILURE);
2551624Spjha 
2561624Spjha 	offset += base;
2571624Spjha 
2581624Spjha 	switch (size) {
2591624Spjha 	case PCI_CAP_CFGSZ_8:
2601624Spjha 		pci_config_put8(h, offset, data);
2611624Spjha 		break;
2621624Spjha 	case PCI_CAP_CFGSZ_16:
2631624Spjha 		pci_config_put16(h, offset, data);
2641624Spjha 		break;
2651624Spjha 	case PCI_CAP_CFGSZ_32:
2661624Spjha 		pci_config_put32(h, offset, data);
2671624Spjha 		break;
2681624Spjha 	default:
2691624Spjha 		return (DDI_FAILURE);
2701624Spjha 	}
2711624Spjha 
2721624Spjha 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
2731624Spjha 	return (DDI_SUCCESS);
2741624Spjha }
2751624Spjha 
2761624Spjha /*
2771624Spjha  * Cache the entire Cap Structure.  The caller is required to allocate and free
2781624Spjha  * buffer.
2791624Spjha  */
2801624Spjha int
2811624Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
2821624Spjha 	uint32_t *buf_p, uint32_t nwords)
2831624Spjha {
2841624Spjha 
2851624Spjha 	int i;
2861624Spjha 	uint32_t *ptr;
2871624Spjha 
2881624Spjha 	ASSERT(nwords < 1024);
2891624Spjha 
2901624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2911624Spjha 		return (DDI_FAILURE);
2921624Spjha 
2931624Spjha 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
2941774Spjha 		if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
2951624Spjha 			return (DDI_FAILURE);
2961624Spjha 	}
2971624Spjha 
2981624Spjha 	return (DDI_SUCCESS);
2991624Spjha }
300