1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008-2013 Solarflare Communications Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/pci.h>
31 #include <sys/pcie.h>
32
33 /* PCIe 2.0 link speeds */
34 #ifndef PCIE_LINKCAP_MAX_SPEED_5_0
35 #define PCIE_LINKCAP_MAX_SPEED_5_0 0x2
36 #endif
37 #ifndef PCIE_LINKSTS_SPEED_5_0
38 #define PCIE_LINKSTS_SPEED_5_0 0x2
39 #endif
40
41 #include "sfxge.h"
42
43 int
44 sfxge_pci_cap_find(sfxge_t *sp, uint8_t cap_id, off_t *offp)
45 {
46 off_t off;
47 uint16_t stat;
48 int rc;
49
50 stat = pci_config_get16(sp->s_pci_handle, PCI_CONF_STAT);
51
52 if (!(stat & PCI_STAT_CAP)) {
53 rc = ENOTSUP;
54 goto fail1;
55 }
56
57 for (off = pci_config_get8(sp->s_pci_handle, PCI_CONF_CAP_PTR);
58 off != PCI_CAP_NEXT_PTR_NULL;
59 off = pci_config_get8(sp->s_pci_handle, off + PCI_CAP_NEXT_PTR)) {
60 if (cap_id == pci_config_get8(sp->s_pci_handle,
61 off + PCI_CAP_ID))
62 goto done;
63 }
64
65 rc = ENOENT;
66 goto fail2;
67
68 done:
69 *offp = off;
70 return (0);
71
72 fail2:
73 DTRACE_PROBE(fail2);
74 fail1:
75 DTRACE_PROBE1(fail1, int, rc);
76
77 return (rc);
78 }
79
80 int
81 sfxge_pci_init(sfxge_t *sp)
82 {
83 off_t off;
84 uint16_t pciecap;
85 uint16_t devctl;
86 uint16_t linksts;
87 uint16_t max_payload_size;
88 uint16_t max_read_request;
89 int rc;
90
91 if (pci_config_setup(sp->s_dip, &(sp->s_pci_handle)) != DDI_SUCCESS) {
92 rc = ENODEV;
93 goto fail1;
94 }
95
96 sp->s_pci_venid = pci_config_get16(sp->s_pci_handle, PCI_CONF_VENID);
97 sp->s_pci_devid = pci_config_get16(sp->s_pci_handle, PCI_CONF_DEVID);
98 if ((rc = efx_family(sp->s_pci_venid, sp->s_pci_devid,
99 &sp->s_family)) != 0)
100 goto fail2;
101
102 if ((rc = sfxge_pci_cap_find(sp, PCI_CAP_ID_PCI_E, &off)) != 0)
103 goto fail3;
104
105 pciecap = pci_config_get16(sp->s_pci_handle, off + PCIE_PCIECAP);
106 ASSERT3U((pciecap & PCIE_PCIECAP_VER_MASK), >=, PCIE_PCIECAP_VER_1_0);
107
108 linksts = pci_config_get16(sp->s_pci_handle, off + PCIE_LINKSTS);
109 switch (linksts & PCIE_LINKSTS_NEG_WIDTH_MASK) {
110 case PCIE_LINKSTS_NEG_WIDTH_X1:
111 sp->s_pcie_nlanes = 1;
112 break;
113
114 case PCIE_LINKSTS_NEG_WIDTH_X2:
115 sp->s_pcie_nlanes = 2;
116 break;
117
118 case PCIE_LINKSTS_NEG_WIDTH_X4:
119 sp->s_pcie_nlanes = 4;
120 break;
121
122 case PCIE_LINKSTS_NEG_WIDTH_X8:
123 sp->s_pcie_nlanes = 8;
124 break;
125
126 default:
127 ASSERT(B_FALSE);
128 break;
129 }
130
131 switch (linksts & PCIE_LINKSTS_SPEED_MASK) {
132 case PCIE_LINKSTS_SPEED_2_5:
133 sp->s_pcie_linkspeed = 1;
134 break;
135
136 case PCIE_LINKSTS_SPEED_5_0:
137 sp->s_pcie_linkspeed = 2;
138 break;
139
140 default:
141 ASSERT(B_FALSE);
142 break;
143 }
144
145 devctl = pci_config_get16(sp->s_pci_handle, off + PCIE_DEVCTL);
146
147 max_payload_size = (devctl & PCIE_DEVCTL_MAX_PAYLOAD_MASK)
148 >> PCIE_DEVCTL_MAX_PAYLOAD_SHIFT;
149
150 max_read_request = (devctl & PCIE_DEVCTL_MAX_READ_REQ_MASK)
151 >> PCIE_DEVCTL_MAX_READ_REQ_SHIFT;
152
153 cmn_err(CE_NOTE,
154 SFXGE_CMN_ERR "PCIe MRR: %d TLP: %d Link: %s Lanes: x%d",
155 128 << max_read_request,
156 128 << max_payload_size,
157 (sp->s_pcie_linkspeed == 1) ? "2.5G" :
158 (sp->s_pcie_linkspeed == 2) ? "5.0G" :
159 "UNKNOWN",
160 sp->s_pcie_nlanes);
161
162 return (0);
163
164 fail3:
165 DTRACE_PROBE(fail3);
166 fail2:
167 DTRACE_PROBE(fail2);
168
169 pci_config_teardown(&(sp->s_pci_handle));
170 sp->s_pci_handle = NULL;
171
172 fail1:
173 DTRACE_PROBE1(fail1, int, rc);
174
175 return (rc);
176 }
177
178 void
179 sfxge_pcie_check_link(sfxge_t *sp, unsigned int full_nlanes,
180 unsigned int full_speed)
181 {
182 if ((sp->s_pcie_linkspeed < full_speed) ||
183 (sp->s_pcie_nlanes < full_nlanes))
184 cmn_err(CE_NOTE,
185 SFXGE_CMN_ERR "The %s%d device requires %d PCIe lanes "
186 "at %s link speed to reach full bandwidth.",
187 ddi_driver_name(sp->s_dip),
188 ddi_get_instance(sp->s_dip),
189 full_nlanes,
190 (full_speed == 1) ? "2.5G" :
191 (full_speed == 2) ? "5.0G" :
192 "UNKNOWN");
193 }
194
195 int
196 sfxge_pci_ioctl(sfxge_t *sp, sfxge_pci_ioc_t *spip)
197 {
198 int rc;
199
200 switch (spip->spi_op) {
201 case SFXGE_PCI_OP_READ:
202 spip->spi_data = pci_config_get8(sp->s_pci_handle,
203 spip->spi_addr);
204 break;
205
206 case SFXGE_PCI_OP_WRITE:
207 pci_config_put8(sp->s_pci_handle,
208 spip->spi_addr, spip->spi_data);
209 break;
210
211 default:
212 rc = ENOTSUP;
213 goto fail1;
214 }
215
216 return (0);
217
218 fail1:
219 DTRACE_PROBE1(fail1, int, rc);
220
221 return (0);
222 }
223
224 void
225 sfxge_pci_fini(sfxge_t *sp)
226 {
227 sp->s_pcie_nlanes = 0;
228 sp->s_pcie_linkspeed = 0;
229
230 pci_config_teardown(&(sp->s_pci_handle));
231 sp->s_pci_handle = NULL;
232 }