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