1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008-2013 Solarflare Communications Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/pci.h>
  31 #include <sys/pcie.h>
  32 
  33 /* PCIe 2.0 link speeds */
  34 #ifndef PCIE_LINKCAP_MAX_SPEED_5_0
  35 #define PCIE_LINKCAP_MAX_SPEED_5_0      0x2
  36 #endif
  37 #ifndef PCIE_LINKSTS_SPEED_5_0
  38 #define PCIE_LINKSTS_SPEED_5_0          0x2
  39 #endif
  40 
  41 #include "sfxge.h"
  42 
  43 int
  44 sfxge_pci_cap_find(sfxge_t *sp, uint8_t cap_id, off_t *offp)
  45 {
  46         off_t off;
  47         uint16_t stat;
  48         int rc;
  49 
  50         stat = pci_config_get16(sp->s_pci_handle, PCI_CONF_STAT);
  51 
  52         if (!(stat & PCI_STAT_CAP)) {
  53                 rc = ENOTSUP;
  54                 goto fail1;
  55         }
  56 
  57         for (off = pci_config_get8(sp->s_pci_handle, PCI_CONF_CAP_PTR);
  58             off != PCI_CAP_NEXT_PTR_NULL;
  59             off = pci_config_get8(sp->s_pci_handle, off + PCI_CAP_NEXT_PTR)) {
  60                 if (cap_id == pci_config_get8(sp->s_pci_handle,
  61                     off + PCI_CAP_ID))
  62                         goto done;
  63         }
  64 
  65         rc = ENOENT;
  66         goto fail2;
  67 
  68 done:
  69         *offp = off;
  70         return (0);
  71 
  72 fail2:
  73         DTRACE_PROBE(fail2);
  74 fail1:
  75         DTRACE_PROBE1(fail1, int, rc);
  76 
  77         return (rc);
  78 }
  79 
  80 int
  81 sfxge_pci_init(sfxge_t *sp)
  82 {
  83         off_t off;
  84         uint16_t pciecap;
  85         uint16_t devctl;
  86         uint16_t linksts;
  87         uint16_t max_payload_size;
  88         uint16_t max_read_request;
  89         int rc;
  90 
  91         if (pci_config_setup(sp->s_dip, &(sp->s_pci_handle)) != DDI_SUCCESS) {
  92                 rc = ENODEV;
  93                 goto fail1;
  94         }
  95 
  96         sp->s_pci_venid = pci_config_get16(sp->s_pci_handle, PCI_CONF_VENID);
  97         sp->s_pci_devid = pci_config_get16(sp->s_pci_handle, PCI_CONF_DEVID);
  98         if ((rc = efx_family(sp->s_pci_venid, sp->s_pci_devid,
  99             &sp->s_family)) != 0)
 100                 goto fail2;
 101 
 102         if ((rc = sfxge_pci_cap_find(sp, PCI_CAP_ID_PCI_E, &off)) != 0)
 103                 goto fail3;
 104 
 105         pciecap = pci_config_get16(sp->s_pci_handle, off + PCIE_PCIECAP);
 106         ASSERT3U((pciecap & PCIE_PCIECAP_VER_MASK), >=, PCIE_PCIECAP_VER_1_0);
 107 
 108         linksts = pci_config_get16(sp->s_pci_handle, off + PCIE_LINKSTS);
 109         switch (linksts & PCIE_LINKSTS_NEG_WIDTH_MASK) {
 110         case PCIE_LINKSTS_NEG_WIDTH_X1:
 111                 sp->s_pcie_nlanes = 1;
 112                 break;
 113 
 114         case PCIE_LINKSTS_NEG_WIDTH_X2:
 115                 sp->s_pcie_nlanes = 2;
 116                 break;
 117 
 118         case PCIE_LINKSTS_NEG_WIDTH_X4:
 119                 sp->s_pcie_nlanes = 4;
 120                 break;
 121 
 122         case PCIE_LINKSTS_NEG_WIDTH_X8:
 123                 sp->s_pcie_nlanes = 8;
 124                 break;
 125 
 126         default:
 127                 ASSERT(B_FALSE);
 128                 break;
 129         }
 130 
 131         switch (linksts & PCIE_LINKSTS_SPEED_MASK) {
 132         case PCIE_LINKSTS_SPEED_2_5:
 133                 sp->s_pcie_linkspeed = 1;
 134                 break;
 135 
 136         case PCIE_LINKSTS_SPEED_5_0:
 137                 sp->s_pcie_linkspeed = 2;
 138                 break;
 139 
 140         default:
 141                 ASSERT(B_FALSE);
 142                 break;
 143         }
 144 
 145         devctl = pci_config_get16(sp->s_pci_handle, off + PCIE_DEVCTL);
 146 
 147         max_payload_size = (devctl & PCIE_DEVCTL_MAX_PAYLOAD_MASK)
 148             >> PCIE_DEVCTL_MAX_PAYLOAD_SHIFT;
 149 
 150         max_read_request = (devctl & PCIE_DEVCTL_MAX_READ_REQ_MASK)
 151             >> PCIE_DEVCTL_MAX_READ_REQ_SHIFT;
 152 
 153         cmn_err(CE_NOTE,
 154             SFXGE_CMN_ERR "PCIe MRR: %d TLP: %d Link: %s Lanes: x%d",
 155             128 << max_read_request,
 156             128 << max_payload_size,
 157             (sp->s_pcie_linkspeed == 1) ? "2.5G" :
 158             (sp->s_pcie_linkspeed == 2) ? "5.0G" :
 159             "UNKNOWN",
 160             sp->s_pcie_nlanes);
 161 
 162         return (0);
 163 
 164 fail3:
 165         DTRACE_PROBE(fail3);
 166 fail2:
 167         DTRACE_PROBE(fail2);
 168 
 169         pci_config_teardown(&(sp->s_pci_handle));
 170         sp->s_pci_handle = NULL;
 171 
 172 fail1:
 173         DTRACE_PROBE1(fail1, int, rc);
 174 
 175         return (rc);
 176 }
 177 
 178 void
 179 sfxge_pcie_check_link(sfxge_t *sp, unsigned int full_nlanes,
 180         unsigned int full_speed)
 181 {
 182         if ((sp->s_pcie_linkspeed < full_speed) ||
 183             (sp->s_pcie_nlanes    < full_nlanes))
 184                 cmn_err(CE_NOTE,
 185                     SFXGE_CMN_ERR "The %s%d device requires %d PCIe lanes "
 186                     "at %s link speed to reach full bandwidth.",
 187                     ddi_driver_name(sp->s_dip),
 188                     ddi_get_instance(sp->s_dip),
 189                     full_nlanes,
 190                     (full_speed == 1) ? "2.5G" :
 191                     (full_speed == 2) ? "5.0G" :
 192                     "UNKNOWN");
 193 }
 194 
 195 int
 196 sfxge_pci_ioctl(sfxge_t *sp, sfxge_pci_ioc_t *spip)
 197 {
 198         int rc;
 199 
 200         switch (spip->spi_op) {
 201         case SFXGE_PCI_OP_READ:
 202                 spip->spi_data = pci_config_get8(sp->s_pci_handle,
 203                     spip->spi_addr);
 204                 break;
 205 
 206         case SFXGE_PCI_OP_WRITE:
 207                 pci_config_put8(sp->s_pci_handle,
 208                     spip->spi_addr, spip->spi_data);
 209                 break;
 210 
 211         default:
 212                 rc = ENOTSUP;
 213                 goto fail1;
 214         }
 215 
 216         return (0);
 217 
 218 fail1:
 219         DTRACE_PROBE1(fail1, int, rc);
 220 
 221         return (0);
 222 }
 223 
 224 void
 225 sfxge_pci_fini(sfxge_t *sp)
 226 {
 227         sp->s_pcie_nlanes = 0;
 228         sp->s_pcie_linkspeed = 0;
 229 
 230         pci_config_teardown(&(sp->s_pci_handle));
 231         sp->s_pci_handle = NULL;
 232 }