xref: /illumos-gate/usr/src/uts/common/io/sfxge/sfxge_pci.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
1 /*
2  * Copyright (c) 2008-2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/pci.h>
35 #include <sys/pcie.h>
36 
37 /* PCIe 3.0 link speeds */
38 #ifndef PCIE_LINKCAP_MAX_SPEED_5
39 #define	PCIE_LINKCAP_MAX_SPEED_5	0x2
40 #endif
41 #ifndef PCIE_LINKSTS_SPEED_5
42 #define	PCIE_LINKSTS_SPEED_5		0x2
43 #endif
44 #ifndef PCIE_LINKCAP_MAX_SPEED_8
45 #define	PCIE_LINKCAP_MAX_SPEED_8	0x3
46 #endif
47 #ifndef PCIE_LINKSTS_SPEED_8
48 #define	PCIE_LINKSTS_SPEED_8		0x3
49 #endif
50 
51 #include "sfxge.h"
52 
53 int
sfxge_pci_cap_find(sfxge_t * sp,uint8_t cap_id,off_t * offp)54 sfxge_pci_cap_find(sfxge_t *sp, uint8_t cap_id, off_t *offp)
55 {
56 	off_t off;
57 	uint16_t stat;
58 	int rc;
59 
60 	stat = pci_config_get16(sp->s_pci_handle, PCI_CONF_STAT);
61 
62 	if (!(stat & PCI_STAT_CAP)) {
63 		rc = ENOTSUP;
64 		goto fail1;
65 	}
66 
67 	for (off = pci_config_get8(sp->s_pci_handle, PCI_CONF_CAP_PTR);
68 	    off != PCI_CAP_NEXT_PTR_NULL;
69 	    off = pci_config_get8(sp->s_pci_handle, off + PCI_CAP_NEXT_PTR)) {
70 		if (cap_id == pci_config_get8(sp->s_pci_handle,
71 		    off + PCI_CAP_ID))
72 			goto done;
73 	}
74 
75 	rc = ENOENT;
76 	goto fail2;
77 
78 done:
79 	*offp = off;
80 	return (0);
81 
82 fail2:
83 	DTRACE_PROBE(fail2);
84 fail1:
85 	DTRACE_PROBE1(fail1, int, rc);
86 
87 	return (rc);
88 }
89 
90 int
sfxge_pci_init(sfxge_t * sp)91 sfxge_pci_init(sfxge_t *sp)
92 {
93 	off_t off;
94 	uint16_t pciecap;
95 	uint16_t devctl;
96 	uint16_t linksts;
97 	uint16_t max_payload_size;
98 	uint16_t max_read_request;
99 	int rc;
100 #if EFSYS_OPT_MCDI_LOGGING
101 	int *pci_regs;
102 	uint_t pci_nregs = 0;
103 
104 	/*
105 	 * We need the PCI bus address to format MCDI logging output in the
106 	 * same way as on other platforms.
107 	 * It appears there's no straightforward way to extract the address
108 	 * from a "dev_info_t" structure, though.
109 	 * The "reg" property is supported by all PCIe devices, and contains
110 	 * an arbitrary length array of elements describing logical
111 	 * resources. Each element contains a 5-tuple of 32bit values,
112 	 * where the first 32bit value contains the bus/dev/fn slot
113 	 * information.
114 	 * See pci(5) and the definition of "struct pci_phys_spec" in sys/pci.h
115 	 */
116 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, sp->s_dip,
117 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_regs, &pci_nregs) !=
118 	    DDI_PROP_SUCCESS) {
119 		rc = ENODEV;
120 		goto fail1;
121 	}
122 	sp->s_bus_addr = pci_regs[0];
123 	ddi_prop_free(pci_regs);
124 #endif
125 
126 	if (pci_config_setup(sp->s_dip, &(sp->s_pci_handle)) != DDI_SUCCESS) {
127 		rc = ENODEV;
128 		goto fail1;
129 	}
130 
131 	sp->s_pci_venid = pci_config_get16(sp->s_pci_handle, PCI_CONF_VENID);
132 	sp->s_pci_devid = pci_config_get16(sp->s_pci_handle, PCI_CONF_DEVID);
133 	if ((rc = efx_family(sp->s_pci_venid, sp->s_pci_devid,
134 	    &sp->s_family)) != 0)
135 		goto fail2;
136 
137 	if ((rc = sfxge_pci_cap_find(sp, PCI_CAP_ID_PCI_E, &off)) != 0)
138 		goto fail3;
139 
140 	pciecap = pci_config_get16(sp->s_pci_handle, off + PCIE_PCIECAP);
141 	ASSERT3U((pciecap & PCIE_PCIECAP_VER_MASK), >=, PCIE_PCIECAP_VER_1_0);
142 
143 	linksts = pci_config_get16(sp->s_pci_handle, off + PCIE_LINKSTS);
144 	switch (linksts & PCIE_LINKSTS_NEG_WIDTH_MASK) {
145 	case PCIE_LINKSTS_NEG_WIDTH_X1:
146 		sp->s_pcie_nlanes = 1;
147 		break;
148 
149 	case PCIE_LINKSTS_NEG_WIDTH_X2:
150 		sp->s_pcie_nlanes = 2;
151 		break;
152 
153 	case PCIE_LINKSTS_NEG_WIDTH_X4:
154 		sp->s_pcie_nlanes = 4;
155 		break;
156 
157 	case PCIE_LINKSTS_NEG_WIDTH_X8:
158 		sp->s_pcie_nlanes = 8;
159 		break;
160 
161 	default:
162 		ASSERT(B_FALSE);
163 		break;
164 	}
165 
166 	switch (linksts & PCIE_LINKSTS_SPEED_MASK) {
167 	case PCIE_LINKSTS_SPEED_2_5:
168 		sp->s_pcie_linkspeed = 1;
169 		break;
170 
171 	case PCIE_LINKSTS_SPEED_5:
172 		sp->s_pcie_linkspeed = 2;
173 		break;
174 
175 	case PCIE_LINKSTS_SPEED_8:
176 		sp->s_pcie_linkspeed = 3;
177 		break;
178 
179 	default:
180 		ASSERT(B_FALSE);
181 		break;
182 	}
183 
184 	devctl = pci_config_get16(sp->s_pci_handle, off + PCIE_DEVCTL);
185 
186 	max_payload_size = (devctl & PCIE_DEVCTL_MAX_PAYLOAD_MASK)
187 	    >> PCIE_DEVCTL_MAX_PAYLOAD_SHIFT;
188 
189 	max_read_request = (devctl & PCIE_DEVCTL_MAX_READ_REQ_MASK)
190 	    >> PCIE_DEVCTL_MAX_READ_REQ_SHIFT;
191 
192 	dev_err(sp->s_dip, CE_NOTE,
193 	    SFXGE_CMN_ERR "PCIe MRR: %d TLP: %d Link: %s Lanes: x%d",
194 	    128 << max_read_request,
195 	    128 << max_payload_size,
196 	    (sp->s_pcie_linkspeed == 1) ? "2.5G" :
197 	    (sp->s_pcie_linkspeed == 2) ? "5.0G" :
198 	    (sp->s_pcie_linkspeed == 3) ? "8.0G" :
199 	    "UNKNOWN",
200 	    sp->s_pcie_nlanes);
201 
202 	return (0);
203 
204 fail3:
205 	DTRACE_PROBE(fail3);
206 fail2:
207 	DTRACE_PROBE(fail2);
208 
209 	pci_config_teardown(&(sp->s_pci_handle));
210 	sp->s_pci_handle = NULL;
211 
212 fail1:
213 	DTRACE_PROBE1(fail1, int, rc);
214 
215 	return (rc);
216 }
217 
218 void
sfxge_pcie_check_link(sfxge_t * sp,unsigned int full_nlanes,unsigned int full_speed)219 sfxge_pcie_check_link(sfxge_t *sp, unsigned int full_nlanes,
220     unsigned int full_speed)
221 {
222 	if ((sp->s_pcie_linkspeed < full_speed) ||
223 	    (sp->s_pcie_nlanes    < full_nlanes))
224 		dev_err(sp->s_dip, CE_NOTE,
225 		    SFXGE_CMN_ERR "This device requires %d PCIe lanes "
226 		    "at %s link speed to reach full bandwidth.",
227 		    full_nlanes,
228 		    (full_speed == 1) ? "2.5G" :
229 		    (full_speed == 2) ? "5.0G" :
230 		    (full_speed == 3) ? "8.0G" :
231 		    "UNKNOWN");
232 }
233 
234 void
sfxge_pci_fini(sfxge_t * sp)235 sfxge_pci_fini(sfxge_t *sp)
236 {
237 	sp->s_pcie_nlanes = 0;
238 	sp->s_pcie_linkspeed = 0;
239 
240 	pci_config_teardown(&(sp->s_pci_handle));
241 	sp->s_pci_handle = NULL;
242 }
243