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
53int
54sfxge_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
78done:
79	*offp = off;
80	return (0);
81
82fail2:
83	DTRACE_PROBE(fail2);
84fail1:
85	DTRACE_PROBE1(fail1, int, rc);
86
87	return (rc);
88}
89
90int
91sfxge_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(4) 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
204fail3:
205	DTRACE_PROBE(fail3);
206fail2:
207	DTRACE_PROBE(fail2);
208
209	pci_config_teardown(&(sp->s_pci_handle));
210	sp->s_pci_handle = NULL;
211
212fail1:
213	DTRACE_PROBE1(fail1, int, rc);
214
215	return (rc);
216}
217
218void
219sfxge_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
234void
235sfxge_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