xref: /titanic_44/usr/src/uts/common/io/pci_cap.c (revision cb7ea99db394f3bd5f4a6c6bf58c8c52df3508f0)
127255037Spjha /*
227255037Spjha  * CDDL HEADER START
327255037Spjha  *
427255037Spjha  * The contents of this file are subject to the terms of the
527255037Spjha  * Common Development and Distribution License (the "License").
627255037Spjha  * You may not use this file except in compliance with the License.
727255037Spjha  *
827255037Spjha  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
927255037Spjha  * or http://www.opensolaris.org/os/licensing.
1027255037Spjha  * See the License for the specific language governing permissions
1127255037Spjha  * and limitations under the License.
1227255037Spjha  *
1327255037Spjha  * When distributing Covered Code, include this CDDL HEADER in each
1427255037Spjha  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1527255037Spjha  * If applicable, add the following below this CDDL HEADER, with the
1627255037Spjha  * fields enclosed by brackets "[]" replaced with your own identifying
1727255037Spjha  * information: Portions Copyright [yyyy] [name of copyright owner]
1827255037Spjha  *
1927255037Spjha  * CDDL HEADER END
2027255037Spjha  */
2127255037Spjha 
2227255037Spjha #include <sys/note.h>
2327255037Spjha #include <sys/conf.h>
2427255037Spjha #include <sys/debug.h>
2527255037Spjha #include <sys/sunddi.h>
2627255037Spjha #include <sys/pci.h>
2727255037Spjha #include <sys/pcie.h>
2827255037Spjha #include <sys/bitmap.h>
2927255037Spjha #include <sys/autoconf.h>
3027255037Spjha #include <sys/sysmacros.h>
3127255037Spjha #include <sys/pci_cap.h>
3227255037Spjha 
3327255037Spjha /*
34fb66942fSCasper H.S. Dik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3527255037Spjha  * Use is subject to license terms.
3627255037Spjha  */
3727255037Spjha 
3827255037Spjha /*
3927255037Spjha  * Generic PCI Capabilites Interface for all pci platforms
4027255037Spjha  */
4127255037Spjha 
4227255037Spjha #ifdef DEBUG
4327255037Spjha uint_t  pci_cap_debug = 0;
4427255037Spjha #endif
4527255037Spjha 
4627255037Spjha /* Cap Base Macro */
4727255037Spjha #define	PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
4827255037Spjha 	(id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
4927255037Spjha 
5027255037Spjha /*
5127255037Spjha  * pci_cap_probe: returns the capid and base based upon a given index
5227255037Spjha  */
5327255037Spjha int
pci_cap_probe(ddi_acc_handle_t h,uint16_t index,uint32_t * id_p,uint16_t * base_p)5427255037Spjha pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
5527255037Spjha 	uint32_t *id_p, uint16_t *base_p)
5627255037Spjha {
5727255037Spjha 	int i, search_ext = 0;
5827255037Spjha 	uint16_t base, pcix_cmd, status;
5927255037Spjha 	uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
6027255037Spjha 
6127255037Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
6227255037Spjha 
63dc5d169bSpjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
6427255037Spjha 		return (DDI_FAILURE);
6527255037Spjha 
6627255037Spjha 	/* PCIE and PCIX Version 2 contain Extended Config Space */
6727255037Spjha 	for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
6827255037Spjha 	    base && i < index; base = pci_config_get8(h, base
6927255037Spjha 	    + PCI_CAP_NEXT_PTR), i++) {
7027255037Spjha 
7127255037Spjha 		if ((id = pci_config_get8(h, base)) == 0xff)
7227255037Spjha 			break;
7327255037Spjha 
7427255037Spjha 		if (id == PCI_CAP_ID_PCI_E)
7527255037Spjha 			search_ext = 1;
7627255037Spjha 		else if (id == PCI_CAP_ID_PCIX) {
7727255037Spjha 			if ((pcix_cmd = pci_config_get16(h, base +
78dc5d169bSpjha 			    PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
7927255037Spjha 				continue;
8027255037Spjha 			if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
8127255037Spjha 				search_ext = 1;
8227255037Spjha 		}
8327255037Spjha 	}
8427255037Spjha 
8527255037Spjha 	if (base && i == index) {
8627255037Spjha 		if ((id = pci_config_get8(h, base)) != 0xff)
8727255037Spjha 			goto found;
8827255037Spjha 	}
8927255037Spjha 
9027255037Spjha 	if (!search_ext)
9127255037Spjha 		return (DDI_FAILURE);
9227255037Spjha 
9327255037Spjha 	for (base = PCIE_EXT_CAP; base && i < index; i++) {
94dc5d169bSpjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
9527255037Spjha 			break;
9627255037Spjha 
9727255037Spjha 		id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
9827255037Spjha 		    & PCIE_EXT_CAP_ID_MASK;
9927255037Spjha 		base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
10027255037Spjha 		    & PCIE_EXT_CAP_NEXT_PTR_MASK;
10127255037Spjha 	}
10227255037Spjha 
10327255037Spjha 	if (!base || i < index)
10427255037Spjha 		return (DDI_FAILURE);
10527255037Spjha 
106dc5d169bSpjha 	if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
10727255037Spjha 		return (DDI_FAILURE);
10827255037Spjha 
10927255037Spjha 	id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
11027255037Spjha 	    PCI_CAP_XCFG_FLAG;
11127255037Spjha found:
11227255037Spjha 	PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
11327255037Spjha 	    index, id, base);
11427255037Spjha 
11527255037Spjha 	*id_p = id;
11627255037Spjha 	*base_p = base;
11727255037Spjha 	return (DDI_SUCCESS);
11827255037Spjha 
11927255037Spjha }
12027255037Spjha 
12127255037Spjha /*
12227255037Spjha  * pci_lcap_locate: Helper function locates a base in conventional config space.
12327255037Spjha  */
12427255037Spjha int
pci_lcap_locate(ddi_acc_handle_t h,uint8_t id,uint16_t * base_p)12527255037Spjha pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
12627255037Spjha {
127fb66942fSCasper H.S. Dik 	uint8_t header;
12827255037Spjha 	uint16_t status, base;
12927255037Spjha 
13027255037Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
13127255037Spjha 
132dc5d169bSpjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
13327255037Spjha 		return (DDI_FAILURE);
13427255037Spjha 
135fb66942fSCasper H.S. Dik 	header = pci_config_get8(h, PCI_CONF_HEADER);
136fb66942fSCasper H.S. Dik 	switch (header & PCI_HEADER_TYPE_M) {
137fb66942fSCasper H.S. Dik 	case PCI_HEADER_ZERO:
138fb66942fSCasper H.S. Dik 		base = PCI_CONF_CAP_PTR;
139fb66942fSCasper H.S. Dik 		break;
140fb66942fSCasper H.S. Dik 	case PCI_HEADER_PPB:
141fb66942fSCasper H.S. Dik 		base = PCI_BCNF_CAP_PTR;
142fb66942fSCasper H.S. Dik 		break;
143fb66942fSCasper H.S. Dik 	case PCI_HEADER_CARDBUS:
144fb66942fSCasper H.S. Dik 		base = PCI_CBUS_CAP_PTR;
145fb66942fSCasper H.S. Dik 		break;
146fb66942fSCasper H.S. Dik 	default:
147fb66942fSCasper H.S. Dik 		cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
148fb66942fSCasper H.S. Dik 		    __func__, header);
149fb66942fSCasper H.S. Dik 		return (DDI_FAILURE);
150fb66942fSCasper H.S. Dik 	}
151fb66942fSCasper H.S. Dik 
152fb66942fSCasper H.S. Dik 	for (base = pci_config_get8(h, base); base;
15327255037Spjha 	    base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
15427255037Spjha 		if (pci_config_get8(h, base) == id) {
15527255037Spjha 			*base_p = base;
15627255037Spjha 			return (DDI_SUCCESS);
15727255037Spjha 		}
15827255037Spjha 	}
15927255037Spjha 
16027255037Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
16127255037Spjha 	return (DDI_FAILURE);
16227255037Spjha }
16327255037Spjha 
16427255037Spjha /*
16527255037Spjha  * pci_xcap_locate: Helper function locates a base in extended config space.
16627255037Spjha  */
16727255037Spjha int
pci_xcap_locate(ddi_acc_handle_t h,uint16_t id,uint16_t * base_p)16827255037Spjha pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
16927255037Spjha {
17027255037Spjha 	uint16_t status, base;
17127255037Spjha 	uint32_t xcaps_hdr;
17227255037Spjha 
17327255037Spjha 	status = pci_config_get16(h, PCI_CONF_STAT);
17427255037Spjha 
175dc5d169bSpjha 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
17627255037Spjha 		return (DDI_FAILURE);
17727255037Spjha 
17827255037Spjha 	for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
17927255037Spjha 	    PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
18027255037Spjha 
181dc5d169bSpjha 		if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
18227255037Spjha 			break;
18327255037Spjha 
18427255037Spjha 		if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
18527255037Spjha 		    PCIE_EXT_CAP_ID_MASK) == id) {
18627255037Spjha 			*base_p = base;
18727255037Spjha 			return (DDI_SUCCESS);
18827255037Spjha 		}
18927255037Spjha 	}
19027255037Spjha 
19127255037Spjha 	*base_p = PCI_CAP_NEXT_PTR_NULL;
19227255037Spjha 	return (DDI_FAILURE);
19327255037Spjha }
19427255037Spjha 
19527255037Spjha /*
196*cb7ea99dSJimmy Vetayases  * There can be multiple pci caps with a Hypertransport technology cap ID
197*cb7ea99dSJimmy Vetayases  * Each is distiguished by a type register in the upper half of the cap
198*cb7ea99dSJimmy Vetayases  * header (the "command" register part).
199*cb7ea99dSJimmy Vetayases  *
200*cb7ea99dSJimmy Vetayases  * This returns the location of a hypertransport capability whose upper
201*cb7ea99dSJimmy Vetayases  * 16-bits of the cap header matches <reg_val> after masking the value
202*cb7ea99dSJimmy Vetayases  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
203*cb7ea99dSJimmy Vetayases  * the first HT cap found
204*cb7ea99dSJimmy Vetayases  */
205*cb7ea99dSJimmy Vetayases int
pci_htcap_locate(ddi_acc_handle_t h,uint16_t reg_mask,uint16_t reg_val,uint16_t * base_p)206*cb7ea99dSJimmy Vetayases pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
207*cb7ea99dSJimmy Vetayases     uint16_t *base_p)
208*cb7ea99dSJimmy Vetayases {
209*cb7ea99dSJimmy Vetayases 	uint8_t header;
210*cb7ea99dSJimmy Vetayases 	uint16_t status, base;
211*cb7ea99dSJimmy Vetayases 
212*cb7ea99dSJimmy Vetayases 	status = pci_config_get16(h, PCI_CONF_STAT);
213*cb7ea99dSJimmy Vetayases 
214*cb7ea99dSJimmy Vetayases 	if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
215*cb7ea99dSJimmy Vetayases 		return (DDI_FAILURE);
216*cb7ea99dSJimmy Vetayases 
217*cb7ea99dSJimmy Vetayases 	header = pci_config_get8(h, PCI_CONF_HEADER);
218*cb7ea99dSJimmy Vetayases 	switch (header & PCI_HEADER_TYPE_M) {
219*cb7ea99dSJimmy Vetayases 	case PCI_HEADER_ZERO:
220*cb7ea99dSJimmy Vetayases 		base = PCI_CONF_CAP_PTR;
221*cb7ea99dSJimmy Vetayases 		break;
222*cb7ea99dSJimmy Vetayases 	case PCI_HEADER_PPB:
223*cb7ea99dSJimmy Vetayases 		base = PCI_BCNF_CAP_PTR;
224*cb7ea99dSJimmy Vetayases 		break;
225*cb7ea99dSJimmy Vetayases 	default:
226*cb7ea99dSJimmy Vetayases 		cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
227*cb7ea99dSJimmy Vetayases 		    __func__, header);
228*cb7ea99dSJimmy Vetayases 		return (DDI_FAILURE);
229*cb7ea99dSJimmy Vetayases 	}
230*cb7ea99dSJimmy Vetayases 
231*cb7ea99dSJimmy Vetayases 	for (base = pci_config_get8(h, base); base;
232*cb7ea99dSJimmy Vetayases 	    base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
233*cb7ea99dSJimmy Vetayases 		if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
234*cb7ea99dSJimmy Vetayases 		    (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
235*cb7ea99dSJimmy Vetayases 		    reg_mask) == reg_val) {
236*cb7ea99dSJimmy Vetayases 			*base_p = base;
237*cb7ea99dSJimmy Vetayases 			return (DDI_SUCCESS);
238*cb7ea99dSJimmy Vetayases 		}
239*cb7ea99dSJimmy Vetayases 	}
240*cb7ea99dSJimmy Vetayases 
241*cb7ea99dSJimmy Vetayases 	*base_p = PCI_CAP_NEXT_PTR_NULL;
242*cb7ea99dSJimmy Vetayases 	return (DDI_FAILURE);
243*cb7ea99dSJimmy Vetayases }
244*cb7ea99dSJimmy Vetayases 
245*cb7ea99dSJimmy Vetayases /*
24627255037Spjha  * pci_cap_get: This function uses the base or capid to get a byte, word,
24727255037Spjha  * or dword. If access by capid is requested, the function uses the capid to
24827255037Spjha  * locate the base. Access by a base results in better performance
24927255037Spjha  * because no cap list traversal is required.
25027255037Spjha  */
25127255037Spjha 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)2523c4226f9Spjha pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
25327255037Spjha 	uint32_t id, uint16_t base, uint16_t offset)
25427255037Spjha {
25527255037Spjha 	uint32_t data;
25627255037Spjha 
25727255037Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
258dc5d169bSpjha 		return (PCI_CAP_EINVAL32);
25927255037Spjha 
26027255037Spjha 	/*
261911fc2e5Spjha 	 * Each access to a PCI Configuration Space should be checked
262911fc2e5Spjha 	 * by the calling function. A returned value of the 2's complement
263911fc2e5Spjha 	 * of -1 indicates that either the device is offlined or it does not
264911fc2e5Spjha 	 * exist.
26527255037Spjha 	 */
26627255037Spjha 	offset += base;
26727255037Spjha 
26827255037Spjha 	switch (size) {
26927255037Spjha 	case PCI_CAP_CFGSZ_8:
270911fc2e5Spjha 		data = pci_config_get8(h, offset);
27127255037Spjha 		break;
27227255037Spjha 	case PCI_CAP_CFGSZ_16:
273911fc2e5Spjha 		data = pci_config_get16(h, offset);
27427255037Spjha 		break;
27527255037Spjha 	case PCI_CAP_CFGSZ_32:
276911fc2e5Spjha 		data = pci_config_get32(h, offset);
27727255037Spjha 		break;
27827255037Spjha 	default:
279dc5d169bSpjha 		data = PCI_CAP_EINVAL32;
28027255037Spjha 	}
28127255037Spjha 
28227255037Spjha 	PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
28327255037Spjha 	return (data);
28427255037Spjha }
28527255037Spjha 
28627255037Spjha /*
28727255037Spjha  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
28827255037Spjha  * or dword. If access by capid is requested, the function uses the capid to
28927255037Spjha  * locate the base. Access by base results in better performance
29027255037Spjha  * because no cap list traversal is required.
29127255037Spjha  */
29227255037Spjha 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)2933c4226f9Spjha pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
29427255037Spjha 	uint32_t id, uint16_t base, uint16_t offset,
29527255037Spjha 	uint32_t data)
29627255037Spjha {
29727255037Spjha 
29827255037Spjha 	/*
29927255037Spjha 	 * use the pci_config_size_t to switch for the appropriate read
30027255037Spjha 	 */
30127255037Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
30227255037Spjha 		return (DDI_FAILURE);
30327255037Spjha 
30427255037Spjha 	offset += base;
30527255037Spjha 
30627255037Spjha 	switch (size) {
30727255037Spjha 	case PCI_CAP_CFGSZ_8:
30827255037Spjha 		pci_config_put8(h, offset, data);
30927255037Spjha 		break;
31027255037Spjha 	case PCI_CAP_CFGSZ_16:
31127255037Spjha 		pci_config_put16(h, offset, data);
31227255037Spjha 		break;
31327255037Spjha 	case PCI_CAP_CFGSZ_32:
31427255037Spjha 		pci_config_put32(h, offset, data);
31527255037Spjha 		break;
31627255037Spjha 	default:
31727255037Spjha 		return (DDI_FAILURE);
31827255037Spjha 	}
31927255037Spjha 
32027255037Spjha 	PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
32127255037Spjha 	return (DDI_SUCCESS);
32227255037Spjha }
32327255037Spjha 
32427255037Spjha /*
32527255037Spjha  * Cache the entire Cap Structure.  The caller is required to allocate and free
32627255037Spjha  * buffer.
32727255037Spjha  */
32827255037Spjha int
pci_cap_read(ddi_acc_handle_t h,uint32_t id,uint16_t base,uint32_t * buf_p,uint32_t nwords)32927255037Spjha pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
33027255037Spjha 	uint32_t *buf_p, uint32_t nwords)
33127255037Spjha {
33227255037Spjha 
33327255037Spjha 	int i;
33427255037Spjha 	uint32_t *ptr;
33527255037Spjha 
33627255037Spjha 	ASSERT(nwords < 1024);
33727255037Spjha 
33827255037Spjha 	if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
33927255037Spjha 		return (DDI_FAILURE);
34027255037Spjha 
34127255037Spjha 	for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
342dc5d169bSpjha 		if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
34327255037Spjha 			return (DDI_FAILURE);
34427255037Spjha 	}
34527255037Spjha 
34627255037Spjha 	return (DDI_SUCCESS);
34727255037Spjha }
348