xref: /onnv-gate/usr/src/uts/common/io/pci_cap.c (revision 1644:f2b7985b5922)
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 /*
341624Spjha  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
351624Spjha  * Use is subject to license terms.
361624Spjha  */
371624Spjha 
381624Spjha #pragma ident	"%Z%%M%	%I%	%E% SMI"
391624Spjha 
401624Spjha /*
411624Spjha  * Generic PCI Capabilites Interface for all pci platforms
421624Spjha  */
431624Spjha 
441624Spjha #ifdef DEBUG
451624Spjha uint_t  pci_cap_debug = 0;
461624Spjha #endif
471624Spjha 
481624Spjha /* Cap Base Macro */
491624Spjha #define	PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
501624Spjha 	(id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
511624Spjha 
521624Spjha /*
531624Spjha  * pci_cap_probe: returns the capid and base based upon a given index
541624Spjha  */
551624Spjha int
561624Spjha pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
571624Spjha 	uint32_t *id_p, uint16_t *base_p)
581624Spjha {
591624Spjha 	int i, search_ext = 0;
601624Spjha 	uint16_t base, pcix_cmd, status;
611624Spjha 	uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
621624Spjha 
631624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
641624Spjha 
651624Spjha 	if (status == 0xffff || !(status & PCI_STAT_CAP))
661624Spjha 		return (DDI_FAILURE);
671624Spjha 
681624Spjha 	/* PCIE and PCIX Version 2 contain Extended Config Space */
691624Spjha 	for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
701624Spjha 		base && i < index; base = pci_config_get8(h, base
711624Spjha 			+ PCI_CAP_NEXT_PTR), i++) {
721624Spjha 
731624Spjha 		if ((id = pci_config_get8(h, base)) == 0xff)
741624Spjha 			break;
751624Spjha 
761624Spjha 		if (id == PCI_CAP_ID_PCI_E)
771624Spjha 			search_ext = 1;
781624Spjha 		else if (id == PCI_CAP_ID_PCIX) {
791624Spjha 			if ((pcix_cmd = pci_config_get16(h, base +
801624Spjha 				PCI_PCIX_COMMAND)) != 0xffff)
811624Spjha 				continue;
821624Spjha 			if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
831624Spjha 				search_ext = 1;
841624Spjha 		}
851624Spjha 	}
861624Spjha 
871624Spjha 	if (base && i == index) {
881624Spjha 		if ((id = pci_config_get8(h, base)) != 0xff)
891624Spjha 			goto found;
901624Spjha 	}
911624Spjha 
921624Spjha 	if (!search_ext)
931624Spjha 		return (DDI_FAILURE);
941624Spjha 
951624Spjha 	for (base = PCIE_EXT_CAP; base && i < index; i++) {
961624Spjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
971624Spjha 			break;
981624Spjha 
991624Spjha 		id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
1001624Spjha 			& PCIE_EXT_CAP_ID_MASK;
1011624Spjha 		base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
1021624Spjha 			& PCIE_EXT_CAP_NEXT_PTR_MASK;
1031624Spjha 	}
1041624Spjha 
1051624Spjha 	if (!base || i < index)
1061624Spjha 		return (DDI_FAILURE);
1071624Spjha 
1081624Spjha 	if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
1091624Spjha 		return (DDI_FAILURE);
1101624Spjha 
1111624Spjha 	id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
1121624Spjha 			PCI_CAP_XCFG_FLAG;
1131624Spjha found:
1141624Spjha 	PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
1151624Spjha 		index, id, base);
1161624Spjha 
1171624Spjha 	*id_p = id;
1181624Spjha 	*base_p = base;
1191624Spjha 	return (DDI_SUCCESS);
1201624Spjha 
1211624Spjha }
1221624Spjha 
1231624Spjha /*
1241624Spjha  * pci_lcap_locate: Helper function locates a base in conventional config space.
1251624Spjha  */
1261624Spjha int
1271624Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
1281624Spjha {
1291624Spjha 	uint16_t status, base;
1301624Spjha 
1311624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
1321624Spjha 
1331624Spjha 	if (status == 0xffff || !(status & PCI_STAT_CAP))
1341624Spjha 		return (DDI_FAILURE);
1351624Spjha 
1361624Spjha 	for (base = pci_config_get8(h, PCI_CONF_CAP_PTR); base;
1371624Spjha 		base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
1381624Spjha 		if (pci_config_get8(h, base) == id) {
1391624Spjha 			*base_p = base;
1401624Spjha 			return (DDI_SUCCESS);
1411624Spjha 		}
1421624Spjha 	}
1431624Spjha 
1441624Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
1451624Spjha 	return (DDI_FAILURE);
1461624Spjha 
1471624Spjha 
1481624Spjha }
1491624Spjha 
1501624Spjha /*
1511624Spjha  * pci_xcap_locate: Helper function locates a base in extended config space.
1521624Spjha  */
1531624Spjha int
1541624Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
1551624Spjha {
1561624Spjha 	uint16_t status, base;
1571624Spjha 	uint32_t xcaps_hdr;
1581624Spjha 
1591624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
1601624Spjha 
1611624Spjha 	if (status == 0xffff || !(status & PCI_STAT_CAP))
1621624Spjha 		return (DDI_FAILURE);
1631624Spjha 
1641624Spjha 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
1651624Spjha 		PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
1661624Spjha 
1671624Spjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == 0xffffffff)
1681624Spjha 			break;
1691624Spjha 
1701624Spjha 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
1711624Spjha 			PCIE_EXT_CAP_ID_MASK) == id) {
1721624Spjha 			*base_p = base;
1731624Spjha 			return (DDI_SUCCESS);
1741624Spjha 		}
1751624Spjha 	}
1761624Spjha 
1771624Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
1781624Spjha 	return (DDI_FAILURE);
1791624Spjha }
1801624Spjha 
1811624Spjha /*
1821624Spjha  * pci_cap_get: This function uses the base or capid to get a byte, word,
1831624Spjha  * or dword. If access by capid is requested, the function uses the capid to
1841624Spjha  * locate the base. Access by a base results in better performance
1851624Spjha  * because no cap list traversal is required.
1861624Spjha  */
1871624Spjha uint32_t
1881624Spjha pci_cap_get(ddi_acc_handle_t h, pci_config_size_t size,
1891624Spjha 	uint32_t id, uint16_t base, uint16_t offset)
1901624Spjha {
1911624Spjha 	uint32_t data;
1921624Spjha 
1931624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
194*1644Spjha 		return (0xffffffff);
1951624Spjha 
1961624Spjha 	/*
197*1644Spjha 	 * Each access to a PCI Configuration Space should be checked
198*1644Spjha 	 * by the calling function. A returned value of the 2's complement
199*1644Spjha 	 * of -1 indicates that either the device is offlined or it does not
200*1644Spjha 	 * exist.
2011624Spjha 	 */
2021624Spjha 	offset += base;
2031624Spjha 
2041624Spjha 	switch (size) {
2051624Spjha 	case PCI_CAP_CFGSZ_8:
206*1644Spjha 		data = pci_config_get8(h, offset);
2071624Spjha 		break;
2081624Spjha 	case PCI_CAP_CFGSZ_16:
209*1644Spjha 		data = pci_config_get16(h, offset);
2101624Spjha 		break;
2111624Spjha 	case PCI_CAP_CFGSZ_32:
212*1644Spjha 		data = pci_config_get32(h, offset);
2131624Spjha 		break;
2141624Spjha 	default:
215*1644Spjha 		data = 0xffffffff;
2161624Spjha 	}
2171624Spjha 
2181624Spjha 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
2191624Spjha 	return (data);
2201624Spjha }
2211624Spjha 
2221624Spjha /*
2231624Spjha  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
2241624Spjha  * or dword. If access by capid is requested, the function uses the capid to
2251624Spjha  * locate the base. Access by base results in better performance
2261624Spjha  * because no cap list traversal is required.
2271624Spjha  */
2281624Spjha int
2291624Spjha pci_cap_put(ddi_acc_handle_t h, pci_config_size_t size,
2301624Spjha 	uint32_t id, uint16_t base, uint16_t offset,
2311624Spjha 	uint32_t data)
2321624Spjha {
2331624Spjha 
2341624Spjha 	/*
2351624Spjha 	 * use the pci_config_size_t to switch for the appropriate read
2361624Spjha 	 */
2371624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2381624Spjha 		return (DDI_FAILURE);
2391624Spjha 
2401624Spjha 	offset += base;
2411624Spjha 
2421624Spjha 	switch (size) {
2431624Spjha 	case PCI_CAP_CFGSZ_8:
2441624Spjha 		pci_config_put8(h, offset, data);
2451624Spjha 		break;
2461624Spjha 	case PCI_CAP_CFGSZ_16:
2471624Spjha 		pci_config_put16(h, offset, data);
2481624Spjha 		break;
2491624Spjha 	case PCI_CAP_CFGSZ_32:
2501624Spjha 		pci_config_put32(h, offset, data);
2511624Spjha 		break;
2521624Spjha 	default:
2531624Spjha 		return (DDI_FAILURE);
2541624Spjha 	}
2551624Spjha 
2561624Spjha 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
2571624Spjha 	return (DDI_SUCCESS);
2581624Spjha }
2591624Spjha 
2601624Spjha /*
2611624Spjha  * Cache the entire Cap Structure.  The caller is required to allocate and free
2621624Spjha  * buffer.
2631624Spjha  */
2641624Spjha int
2651624Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
2661624Spjha 	uint32_t *buf_p, uint32_t nwords)
2671624Spjha {
2681624Spjha 
2691624Spjha 	int i;
2701624Spjha 	uint32_t *ptr;
2711624Spjha 
2721624Spjha 	ASSERT(nwords < 1024);
2731624Spjha 
2741624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2751624Spjha 		return (DDI_FAILURE);
2761624Spjha 
2771624Spjha 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
2781624Spjha 		if ((*ptr++ = pci_config_get32(h, base)) == 0xffffffff)
2791624Spjha 			return (DDI_FAILURE);
2801624Spjha 	}
2811624Spjha 
2821624Spjha 	return (DDI_SUCCESS);
2831624Spjha }
284