xref: /onnv-gate/usr/src/uts/common/io/pci_cap.c (revision 9970:565d92d48652)
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 /*
349284SCasper.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
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)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);
68*9970SJimmy.Vetayases@Sun.COM 	    base && i < index; base = pci_config_get8(h, base
69*9970SJimmy.Vetayases@Sun.COM 	    + 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 +
78*9970SJimmy.Vetayases@Sun.COM 			    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)
98*9970SJimmy.Vetayases@Sun.COM 		    & PCIE_EXT_CAP_ID_MASK;
991624Spjha 		base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
100*9970SJimmy.Vetayases@Sun.COM 		    & 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) |
110*9970SJimmy.Vetayases@Sun.COM 	    PCI_CAP_XCFG_FLAG;
1111624Spjha found:
1121624Spjha 	PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
113*9970SJimmy.Vetayases@Sun.COM 	    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
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)1251624Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
1261624Spjha {
1279284SCasper.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 
1359284SCasper.Dik@Sun.COM 	header = pci_config_get8(h, PCI_CONF_HEADER);
1369284SCasper.Dik@Sun.COM 	switch (header & PCI_HEADER_TYPE_M) {
1379284SCasper.Dik@Sun.COM 	case PCI_HEADER_ZERO:
1389284SCasper.Dik@Sun.COM 		base = PCI_CONF_CAP_PTR;
1399284SCasper.Dik@Sun.COM 		break;
1409284SCasper.Dik@Sun.COM 	case PCI_HEADER_PPB:
1419284SCasper.Dik@Sun.COM 		base = PCI_BCNF_CAP_PTR;
1429284SCasper.Dik@Sun.COM 		break;
1439284SCasper.Dik@Sun.COM 	case PCI_HEADER_CARDBUS:
1449284SCasper.Dik@Sun.COM 		base = PCI_CBUS_CAP_PTR;
1459284SCasper.Dik@Sun.COM 		break;
1469284SCasper.Dik@Sun.COM 	default:
1479284SCasper.Dik@Sun.COM 		cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
148*9970SJimmy.Vetayases@Sun.COM 		    __func__, header);
1499284SCasper.Dik@Sun.COM 		return (DDI_FAILURE);
1509284SCasper.Dik@Sun.COM 	}
1519284SCasper.Dik@Sun.COM 
1529284SCasper.Dik@Sun.COM 	for (base = pci_config_get8(h, base); base;
153*9970SJimmy.Vetayases@Sun.COM 	    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  * pci_xcap_locate: Helper function locates a base in extended config space.
1661624Spjha  */
1671624Spjha int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)1681624Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
1691624Spjha {
1701624Spjha 	uint16_t status, base;
1711624Spjha 	uint32_t xcaps_hdr;
1721624Spjha 
1731624Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
1741624Spjha 
1751774Spjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
1761624Spjha 		return (DDI_FAILURE);
1771624Spjha 
1781624Spjha 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
179*9970SJimmy.Vetayases@Sun.COM 	    PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
1801624Spjha 
1811774Spjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
1821624Spjha 			break;
1831624Spjha 
1841624Spjha 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
185*9970SJimmy.Vetayases@Sun.COM 		    PCIE_EXT_CAP_ID_MASK) == id) {
186*9970SJimmy.Vetayases@Sun.COM 			*base_p = base;
187*9970SJimmy.Vetayases@Sun.COM 			return (DDI_SUCCESS);
188*9970SJimmy.Vetayases@Sun.COM 		}
189*9970SJimmy.Vetayases@Sun.COM 	}
190*9970SJimmy.Vetayases@Sun.COM 
191*9970SJimmy.Vetayases@Sun.COM 	*base_p = PCI_CAP_NEXT_PTR_NULL;
192*9970SJimmy.Vetayases@Sun.COM 	return (DDI_FAILURE);
193*9970SJimmy.Vetayases@Sun.COM }
194*9970SJimmy.Vetayases@Sun.COM 
195*9970SJimmy.Vetayases@Sun.COM /*
196*9970SJimmy.Vetayases@Sun.COM  * There can be multiple pci caps with a Hypertransport technology cap ID
197*9970SJimmy.Vetayases@Sun.COM  * Each is distiguished by a type register in the upper half of the cap
198*9970SJimmy.Vetayases@Sun.COM  * header (the "command" register part).
199*9970SJimmy.Vetayases@Sun.COM  *
200*9970SJimmy.Vetayases@Sun.COM  * This returns the location of a hypertransport capability whose upper
201*9970SJimmy.Vetayases@Sun.COM  * 16-bits of the cap header matches <reg_val> after masking the value
202*9970SJimmy.Vetayases@Sun.COM  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
203*9970SJimmy.Vetayases@Sun.COM  * the first HT cap found
204*9970SJimmy.Vetayases@Sun.COM  */
205*9970SJimmy.Vetayases@Sun.COM int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)206*9970SJimmy.Vetayases@Sun.COM pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
207*9970SJimmy.Vetayases@Sun.COM     uint16_t *base_p)
208*9970SJimmy.Vetayases@Sun.COM {
209*9970SJimmy.Vetayases@Sun.COM 	uint8_t header;
210*9970SJimmy.Vetayases@Sun.COM 	uint16_t status, base;
211*9970SJimmy.Vetayases@Sun.COM 
212*9970SJimmy.Vetayases@Sun.COM 	status = pci_config_get16(h, PCI_CONF_STAT);
213*9970SJimmy.Vetayases@Sun.COM 
214*9970SJimmy.Vetayases@Sun.COM 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
215*9970SJimmy.Vetayases@Sun.COM 		return (DDI_FAILURE);
216*9970SJimmy.Vetayases@Sun.COM 
217*9970SJimmy.Vetayases@Sun.COM 	header = pci_config_get8(h, PCI_CONF_HEADER);
218*9970SJimmy.Vetayases@Sun.COM 	switch (header & PCI_HEADER_TYPE_M) {
219*9970SJimmy.Vetayases@Sun.COM 	case PCI_HEADER_ZERO:
220*9970SJimmy.Vetayases@Sun.COM 		base = PCI_CONF_CAP_PTR;
221*9970SJimmy.Vetayases@Sun.COM 		break;
222*9970SJimmy.Vetayases@Sun.COM 	case PCI_HEADER_PPB:
223*9970SJimmy.Vetayases@Sun.COM 		base = PCI_BCNF_CAP_PTR;
224*9970SJimmy.Vetayases@Sun.COM 		break;
225*9970SJimmy.Vetayases@Sun.COM 	default:
226*9970SJimmy.Vetayases@Sun.COM 		cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
227*9970SJimmy.Vetayases@Sun.COM 		    __func__, header);
228*9970SJimmy.Vetayases@Sun.COM 		return (DDI_FAILURE);
229*9970SJimmy.Vetayases@Sun.COM 	}
230*9970SJimmy.Vetayases@Sun.COM 
231*9970SJimmy.Vetayases@Sun.COM 	for (base = pci_config_get8(h, base); base;
232*9970SJimmy.Vetayases@Sun.COM 	    base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
233*9970SJimmy.Vetayases@Sun.COM 		if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
234*9970SJimmy.Vetayases@Sun.COM 		    (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
235*9970SJimmy.Vetayases@Sun.COM 		    reg_mask) == reg_val) {
2361624Spjha 			*base_p = base;
2371624Spjha 			return (DDI_SUCCESS);
2381624Spjha 		}
2391624Spjha 	}
2401624Spjha 
2411624Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
2421624Spjha 	return (DDI_FAILURE);
2431624Spjha }
2441624Spjha 
2451624Spjha /*
2461624Spjha  * pci_cap_get: This function uses the base or capid to get a byte, word,
2471624Spjha  * or dword. If access by capid is requested, the function uses the capid to
2481624Spjha  * locate the base. Access by a base results in better performance
2491624Spjha  * because no cap list traversal is required.
2501624Spjha  */
2511624Spjha uint32_t
pci_cap_get(ddi_acc_handle_t h,pci_cap_config_size_t size,uint32_t id,uint16_t base,uint16_t offset)2522587Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
2531624Spjha 	uint32_t id, uint16_t base, uint16_t offset)
2541624Spjha {
2551624Spjha 	uint32_t data;
2561624Spjha 
2571624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
2581774Spjha 		return (PCI_CAP_EINVAL32);
2591624Spjha 
2601624Spjha 	/*
2611644Spjha 	 * Each access to a PCI Configuration Space should be checked
2621644Spjha 	 * by the calling function. A returned value of the 2's complement
2631644Spjha 	 * of -1 indicates that either the device is offlined or it does not
2641644Spjha 	 * exist.
2651624Spjha 	 */
2661624Spjha 	offset += base;
2671624Spjha 
2681624Spjha 	switch (size) {
2691624Spjha 	case PCI_CAP_CFGSZ_8:
2701644Spjha 		data = pci_config_get8(h, offset);
2711624Spjha 		break;
2721624Spjha 	case PCI_CAP_CFGSZ_16:
2731644Spjha 		data = pci_config_get16(h, offset);
2741624Spjha 		break;
2751624Spjha 	case PCI_CAP_CFGSZ_32:
2761644Spjha 		data = pci_config_get32(h, offset);
2771624Spjha 		break;
2781624Spjha 	default:
2791774Spjha 		data = PCI_CAP_EINVAL32;
2801624Spjha 	}
2811624Spjha 
2821624Spjha 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
2831624Spjha 	return (data);
2841624Spjha }
2851624Spjha 
2861624Spjha /*
2871624Spjha  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
2881624Spjha  * or dword. If access by capid is requested, the function uses the capid to
2891624Spjha  * locate the base. Access by base results in better performance
2901624Spjha  * because no cap list traversal is required.
2911624Spjha  */
2921624Spjha int
pci_cap_put(ddi_acc_handle_t h,pci_cap_config_size_t size,uint32_t id,uint16_t base,uint16_t offset,uint32_t data)2932587Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
2941624Spjha 	uint32_t id, uint16_t base, uint16_t offset,
2951624Spjha 	uint32_t data)
2961624Spjha {
2971624Spjha 
2981624Spjha 	/*
2991624Spjha 	 * use the pci_config_size_t to switch for the appropriate read
3001624Spjha 	 */
3011624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
3021624Spjha 		return (DDI_FAILURE);
3031624Spjha 
3041624Spjha 	offset += base;
3051624Spjha 
3061624Spjha 	switch (size) {
3071624Spjha 	case PCI_CAP_CFGSZ_8:
3081624Spjha 		pci_config_put8(h, offset, data);
3091624Spjha 		break;
3101624Spjha 	case PCI_CAP_CFGSZ_16:
3111624Spjha 		pci_config_put16(h, offset, data);
3121624Spjha 		break;
3131624Spjha 	case PCI_CAP_CFGSZ_32:
3141624Spjha 		pci_config_put32(h, offset, data);
3151624Spjha 		break;
3161624Spjha 	default:
3171624Spjha 		return (DDI_FAILURE);
3181624Spjha 	}
3191624Spjha 
3201624Spjha 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
3211624Spjha 	return (DDI_SUCCESS);
3221624Spjha }
3231624Spjha 
3241624Spjha /*
3251624Spjha  * Cache the entire Cap Structure.  The caller is required to allocate and free
3261624Spjha  * buffer.
3271624Spjha  */
3281624Spjha int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)3291624Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
3301624Spjha 	uint32_t *buf_p, uint32_t nwords)
3311624Spjha {
3321624Spjha 
3331624Spjha 	int i;
3341624Spjha 	uint32_t *ptr;
3351624Spjha 
3361624Spjha 	ASSERT(nwords < 1024);
3371624Spjha 
3381624Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
3391624Spjha 		return (DDI_FAILURE);
3401624Spjha 
3411624Spjha 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
3421774Spjha 		if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
3431624Spjha 			return (DDI_FAILURE);
3441624Spjha 	}
3451624Spjha 
3461624Spjha 	return (DDI_SUCCESS);
3471624Spjha }
348