149ef7e06SGarrett D'Amore /*
249ef7e06SGarrett D'Amore  * Copyright (c) 2008-2016 Solarflare Communications Inc.
349ef7e06SGarrett D'Amore  * All rights reserved.
449ef7e06SGarrett D'Amore  *
549ef7e06SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
649ef7e06SGarrett D'Amore  * modification, are permitted provided that the following conditions are met:
749ef7e06SGarrett D'Amore  *
849ef7e06SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright notice,
949ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer.
1049ef7e06SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright notice,
1149ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer in the documentation
1249ef7e06SGarrett D'Amore  *    and/or other materials provided with the distribution.
1349ef7e06SGarrett D'Amore  *
1449ef7e06SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1549ef7e06SGarrett D'Amore  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1649ef7e06SGarrett D'Amore  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1749ef7e06SGarrett D'Amore  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
1849ef7e06SGarrett D'Amore  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1949ef7e06SGarrett D'Amore  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2049ef7e06SGarrett D'Amore  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2149ef7e06SGarrett D'Amore  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2249ef7e06SGarrett D'Amore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2349ef7e06SGarrett D'Amore  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2449ef7e06SGarrett D'Amore  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2549ef7e06SGarrett D'Amore  *
2649ef7e06SGarrett D'Amore  * The views and conclusions contained in the software and documentation are
2749ef7e06SGarrett D'Amore  * those of the authors and should not be interpreted as representing official
2849ef7e06SGarrett D'Amore  * policies, either expressed or implied, of the FreeBSD Project.
2949ef7e06SGarrett D'Amore  */
3049ef7e06SGarrett D'Amore 
3149ef7e06SGarrett D'Amore #include <sys/types.h>
3249ef7e06SGarrett D'Amore #include <sys/ddi.h>
3349ef7e06SGarrett D'Amore #include <sys/sunddi.h>
3449ef7e06SGarrett D'Amore #include <sys/pci.h>
3549ef7e06SGarrett D'Amore #include <sys/pcie.h>
3649ef7e06SGarrett D'Amore 
3749ef7e06SGarrett D'Amore /* PCIe 3.0 link speeds */
3849ef7e06SGarrett D'Amore #ifndef PCIE_LINKCAP_MAX_SPEED_5
3949ef7e06SGarrett D'Amore #define	PCIE_LINKCAP_MAX_SPEED_5	0x2
4049ef7e06SGarrett D'Amore #endif
4149ef7e06SGarrett D'Amore #ifndef PCIE_LINKSTS_SPEED_5
4249ef7e06SGarrett D'Amore #define	PCIE_LINKSTS_SPEED_5		0x2
4349ef7e06SGarrett D'Amore #endif
4449ef7e06SGarrett D'Amore #ifndef PCIE_LINKCAP_MAX_SPEED_8
4549ef7e06SGarrett D'Amore #define	PCIE_LINKCAP_MAX_SPEED_8	0x3
4649ef7e06SGarrett D'Amore #endif
4749ef7e06SGarrett D'Amore #ifndef PCIE_LINKSTS_SPEED_8
4849ef7e06SGarrett D'Amore #define	PCIE_LINKSTS_SPEED_8		0x3
4949ef7e06SGarrett D'Amore #endif
5049ef7e06SGarrett D'Amore 
5149ef7e06SGarrett D'Amore #include "sfxge.h"
5249ef7e06SGarrett D'Amore 
5349ef7e06SGarrett D'Amore int
sfxge_pci_cap_find(sfxge_t * sp,uint8_t cap_id,off_t * offp)5449ef7e06SGarrett D'Amore sfxge_pci_cap_find(sfxge_t *sp, uint8_t cap_id, off_t *offp)
5549ef7e06SGarrett D'Amore {
5649ef7e06SGarrett D'Amore 	off_t off;
5749ef7e06SGarrett D'Amore 	uint16_t stat;
5849ef7e06SGarrett D'Amore 	int rc;
5949ef7e06SGarrett D'Amore 
6049ef7e06SGarrett D'Amore 	stat = pci_config_get16(sp->s_pci_handle, PCI_CONF_STAT);
6149ef7e06SGarrett D'Amore 
6249ef7e06SGarrett D'Amore 	if (!(stat & PCI_STAT_CAP)) {
6349ef7e06SGarrett D'Amore 		rc = ENOTSUP;
6449ef7e06SGarrett D'Amore 		goto fail1;
6549ef7e06SGarrett D'Amore 	}
6649ef7e06SGarrett D'Amore 
6749ef7e06SGarrett D'Amore 	for (off = pci_config_get8(sp->s_pci_handle, PCI_CONF_CAP_PTR);
6849ef7e06SGarrett D'Amore 	    off != PCI_CAP_NEXT_PTR_NULL;
6949ef7e06SGarrett D'Amore 	    off = pci_config_get8(sp->s_pci_handle, off + PCI_CAP_NEXT_PTR)) {
7049ef7e06SGarrett D'Amore 		if (cap_id == pci_config_get8(sp->s_pci_handle,
7149ef7e06SGarrett D'Amore 		    off + PCI_CAP_ID))
7249ef7e06SGarrett D'Amore 			goto done;
7349ef7e06SGarrett D'Amore 	}
7449ef7e06SGarrett D'Amore 
7549ef7e06SGarrett D'Amore 	rc = ENOENT;
7649ef7e06SGarrett D'Amore 	goto fail2;
7749ef7e06SGarrett D'Amore 
7849ef7e06SGarrett D'Amore done:
7949ef7e06SGarrett D'Amore 	*offp = off;
8049ef7e06SGarrett D'Amore 	return (0);
8149ef7e06SGarrett D'Amore 
8249ef7e06SGarrett D'Amore fail2:
8349ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
8449ef7e06SGarrett D'Amore fail1:
8549ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
8649ef7e06SGarrett D'Amore 
8749ef7e06SGarrett D'Amore 	return (rc);
8849ef7e06SGarrett D'Amore }
8949ef7e06SGarrett D'Amore 
9049ef7e06SGarrett D'Amore int
sfxge_pci_init(sfxge_t * sp)9149ef7e06SGarrett D'Amore sfxge_pci_init(sfxge_t *sp)
9249ef7e06SGarrett D'Amore {
9349ef7e06SGarrett D'Amore 	off_t off;
9449ef7e06SGarrett D'Amore 	uint16_t pciecap;
9549ef7e06SGarrett D'Amore 	uint16_t devctl;
9649ef7e06SGarrett D'Amore 	uint16_t linksts;
9749ef7e06SGarrett D'Amore 	uint16_t max_payload_size;
9849ef7e06SGarrett D'Amore 	uint16_t max_read_request;
9949ef7e06SGarrett D'Amore 	int rc;
10049ef7e06SGarrett D'Amore #if EFSYS_OPT_MCDI_LOGGING
10149ef7e06SGarrett D'Amore 	int *pci_regs;
10249ef7e06SGarrett D'Amore 	uint_t pci_nregs = 0;
10349ef7e06SGarrett D'Amore 
10449ef7e06SGarrett D'Amore 	/*
10549ef7e06SGarrett D'Amore 	 * We need the PCI bus address to format MCDI logging output in the
10649ef7e06SGarrett D'Amore 	 * same way as on other platforms.
10749ef7e06SGarrett D'Amore 	 * It appears there's no straightforward way to extract the address
10849ef7e06SGarrett D'Amore 	 * from a "dev_info_t" structure, though.
10949ef7e06SGarrett D'Amore 	 * The "reg" property is supported by all PCIe devices, and contains
11049ef7e06SGarrett D'Amore 	 * an arbitrary length array of elements describing logical
11149ef7e06SGarrett D'Amore 	 * resources. Each element contains a 5-tuple of 32bit values,
11249ef7e06SGarrett D'Amore 	 * where the first 32bit value contains the bus/dev/fn slot
11349ef7e06SGarrett D'Amore 	 * information.
114*bbf21555SRichard Lowe 	 * See pci(5) and the definition of "struct pci_phys_spec" in sys/pci.h
11549ef7e06SGarrett D'Amore 	 */
11649ef7e06SGarrett D'Amore 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, sp->s_dip,
11749ef7e06SGarrett D'Amore 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_regs, &pci_nregs) !=
11849ef7e06SGarrett D'Amore 	    DDI_PROP_SUCCESS) {
11949ef7e06SGarrett D'Amore 		rc = ENODEV;
12049ef7e06SGarrett D'Amore 		goto fail1;
12149ef7e06SGarrett D'Amore 	}
12249ef7e06SGarrett D'Amore 	sp->s_bus_addr = pci_regs[0];
12349ef7e06SGarrett D'Amore 	ddi_prop_free(pci_regs);
12449ef7e06SGarrett D'Amore #endif
12549ef7e06SGarrett D'Amore 
12649ef7e06SGarrett D'Amore 	if (pci_config_setup(sp->s_dip, &(sp->s_pci_handle)) != DDI_SUCCESS) {
12749ef7e06SGarrett D'Amore 		rc = ENODEV;
12849ef7e06SGarrett D'Amore 		goto fail1;
12949ef7e06SGarrett D'Amore 	}
13049ef7e06SGarrett D'Amore 
13149ef7e06SGarrett D'Amore 	sp->s_pci_venid = pci_config_get16(sp->s_pci_handle, PCI_CONF_VENID);
13249ef7e06SGarrett D'Amore 	sp->s_pci_devid = pci_config_get16(sp->s_pci_handle, PCI_CONF_DEVID);
13349ef7e06SGarrett D'Amore 	if ((rc = efx_family(sp->s_pci_venid, sp->s_pci_devid,
13449ef7e06SGarrett D'Amore 	    &sp->s_family)) != 0)
13549ef7e06SGarrett D'Amore 		goto fail2;
13649ef7e06SGarrett D'Amore 
13749ef7e06SGarrett D'Amore 	if ((rc = sfxge_pci_cap_find(sp, PCI_CAP_ID_PCI_E, &off)) != 0)
13849ef7e06SGarrett D'Amore 		goto fail3;
13949ef7e06SGarrett D'Amore 
14049ef7e06SGarrett D'Amore 	pciecap = pci_config_get16(sp->s_pci_handle, off + PCIE_PCIECAP);
14149ef7e06SGarrett D'Amore 	ASSERT3U((pciecap & PCIE_PCIECAP_VER_MASK), >=, PCIE_PCIECAP_VER_1_0);
14249ef7e06SGarrett D'Amore 
14349ef7e06SGarrett D'Amore 	linksts = pci_config_get16(sp->s_pci_handle, off + PCIE_LINKSTS);
14449ef7e06SGarrett D'Amore 	switch (linksts & PCIE_LINKSTS_NEG_WIDTH_MASK) {
14549ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_NEG_WIDTH_X1:
14649ef7e06SGarrett D'Amore 		sp->s_pcie_nlanes = 1;
14749ef7e06SGarrett D'Amore 		break;
14849ef7e06SGarrett D'Amore 
14949ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_NEG_WIDTH_X2:
15049ef7e06SGarrett D'Amore 		sp->s_pcie_nlanes = 2;
15149ef7e06SGarrett D'Amore 		break;
15249ef7e06SGarrett D'Amore 
15349ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_NEG_WIDTH_X4:
15449ef7e06SGarrett D'Amore 		sp->s_pcie_nlanes = 4;
15549ef7e06SGarrett D'Amore 		break;
15649ef7e06SGarrett D'Amore 
15749ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_NEG_WIDTH_X8:
15849ef7e06SGarrett D'Amore 		sp->s_pcie_nlanes = 8;
15949ef7e06SGarrett D'Amore 		break;
16049ef7e06SGarrett D'Amore 
16149ef7e06SGarrett D'Amore 	default:
16249ef7e06SGarrett D'Amore 		ASSERT(B_FALSE);
16349ef7e06SGarrett D'Amore 		break;
16449ef7e06SGarrett D'Amore 	}
16549ef7e06SGarrett D'Amore 
16649ef7e06SGarrett D'Amore 	switch (linksts & PCIE_LINKSTS_SPEED_MASK) {
16749ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_SPEED_2_5:
16849ef7e06SGarrett D'Amore 		sp->s_pcie_linkspeed = 1;
16949ef7e06SGarrett D'Amore 		break;
17049ef7e06SGarrett D'Amore 
17149ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_SPEED_5:
17249ef7e06SGarrett D'Amore 		sp->s_pcie_linkspeed = 2;
17349ef7e06SGarrett D'Amore 		break;
17449ef7e06SGarrett D'Amore 
17549ef7e06SGarrett D'Amore 	case PCIE_LINKSTS_SPEED_8:
17649ef7e06SGarrett D'Amore 		sp->s_pcie_linkspeed = 3;
17749ef7e06SGarrett D'Amore 		break;
17849ef7e06SGarrett D'Amore 
17949ef7e06SGarrett D'Amore 	default:
18049ef7e06SGarrett D'Amore 		ASSERT(B_FALSE);
18149ef7e06SGarrett D'Amore 		break;
18249ef7e06SGarrett D'Amore 	}
18349ef7e06SGarrett D'Amore 
18449ef7e06SGarrett D'Amore 	devctl = pci_config_get16(sp->s_pci_handle, off + PCIE_DEVCTL);
18549ef7e06SGarrett D'Amore 
18649ef7e06SGarrett D'Amore 	max_payload_size = (devctl & PCIE_DEVCTL_MAX_PAYLOAD_MASK)
18749ef7e06SGarrett D'Amore 	    >> PCIE_DEVCTL_MAX_PAYLOAD_SHIFT;
18849ef7e06SGarrett D'Amore 
18949ef7e06SGarrett D'Amore 	max_read_request = (devctl & PCIE_DEVCTL_MAX_READ_REQ_MASK)
19049ef7e06SGarrett D'Amore 	    >> PCIE_DEVCTL_MAX_READ_REQ_SHIFT;
19149ef7e06SGarrett D'Amore 
19249ef7e06SGarrett D'Amore 	dev_err(sp->s_dip, CE_NOTE,
19349ef7e06SGarrett D'Amore 	    SFXGE_CMN_ERR "PCIe MRR: %d TLP: %d Link: %s Lanes: x%d",
19449ef7e06SGarrett D'Amore 	    128 << max_read_request,
19549ef7e06SGarrett D'Amore 	    128 << max_payload_size,
19649ef7e06SGarrett D'Amore 	    (sp->s_pcie_linkspeed == 1) ? "2.5G" :
19749ef7e06SGarrett D'Amore 	    (sp->s_pcie_linkspeed == 2) ? "5.0G" :
19849ef7e06SGarrett D'Amore 	    (sp->s_pcie_linkspeed == 3) ? "8.0G" :
19949ef7e06SGarrett D'Amore 	    "UNKNOWN",
20049ef7e06SGarrett D'Amore 	    sp->s_pcie_nlanes);
20149ef7e06SGarrett D'Amore 
20249ef7e06SGarrett D'Amore 	return (0);
20349ef7e06SGarrett D'Amore 
20449ef7e06SGarrett D'Amore fail3:
20549ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail3);
20649ef7e06SGarrett D'Amore fail2:
20749ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
20849ef7e06SGarrett D'Amore 
20949ef7e06SGarrett D'Amore 	pci_config_teardown(&(sp->s_pci_handle));
21049ef7e06SGarrett D'Amore 	sp->s_pci_handle = NULL;
21149ef7e06SGarrett D'Amore 
21249ef7e06SGarrett D'Amore fail1:
21349ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
21449ef7e06SGarrett D'Amore 
21549ef7e06SGarrett D'Amore 	return (rc);
21649ef7e06SGarrett D'Amore }
21749ef7e06SGarrett D'Amore 
21849ef7e06SGarrett D'Amore void
sfxge_pcie_check_link(sfxge_t * sp,unsigned int full_nlanes,unsigned int full_speed)21949ef7e06SGarrett D'Amore sfxge_pcie_check_link(sfxge_t *sp, unsigned int full_nlanes,
22049ef7e06SGarrett D'Amore     unsigned int full_speed)
22149ef7e06SGarrett D'Amore {
22249ef7e06SGarrett D'Amore 	if ((sp->s_pcie_linkspeed < full_speed) ||
22349ef7e06SGarrett D'Amore 	    (sp->s_pcie_nlanes    < full_nlanes))
22449ef7e06SGarrett D'Amore 		dev_err(sp->s_dip, CE_NOTE,
22549ef7e06SGarrett D'Amore 		    SFXGE_CMN_ERR "This device requires %d PCIe lanes "
22649ef7e06SGarrett D'Amore 		    "at %s link speed to reach full bandwidth.",
22749ef7e06SGarrett D'Amore 		    full_nlanes,
22849ef7e06SGarrett D'Amore 		    (full_speed == 1) ? "2.5G" :
22949ef7e06SGarrett D'Amore 		    (full_speed == 2) ? "5.0G" :
23049ef7e06SGarrett D'Amore 		    (full_speed == 3) ? "8.0G" :
23149ef7e06SGarrett D'Amore 		    "UNKNOWN");
23249ef7e06SGarrett D'Amore }
23349ef7e06SGarrett D'Amore 
23449ef7e06SGarrett D'Amore void
sfxge_pci_fini(sfxge_t * sp)23549ef7e06SGarrett D'Amore sfxge_pci_fini(sfxge_t *sp)
23649ef7e06SGarrett D'Amore {
23749ef7e06SGarrett D'Amore 	sp->s_pcie_nlanes = 0;
23849ef7e06SGarrett D'Amore 	sp->s_pcie_linkspeed = 0;
23949ef7e06SGarrett D'Amore 
24049ef7e06SGarrett D'Amore 	pci_config_teardown(&(sp->s_pci_handle));
24149ef7e06SGarrett D'Amore 	sp->s_pci_handle = NULL;
24249ef7e06SGarrett D'Amore }
243