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 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 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(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 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 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 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