1 /*
   2  * Copyright (c) 2008-2015 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 }