1 /*
   2  * Copyright (c) 2009-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/stream.h>
  35 #include <sys/dlpi.h>
  36 #include <sys/pci.h>
  37 
  38 #include "sfxge.h"
  39 #include "efsys.h"
  40 #include "efx.h"
  41 #include "efx_mcdi.h"
  42 #include "efx_regs_mcdi.h"
  43 
  44 /* MAC DMA attributes */
  45 static ddi_device_acc_attr_t sfxge_mcdi_devacc = {
  46 
  47         DDI_DEVICE_ATTR_V0,     /* devacc_attr_version */
  48         DDI_NEVERSWAP_ACC,      /* devacc_attr_endian_flags */
  49         DDI_STRICTORDER_ACC     /* devacc_attr_dataorder */
  50 };
  51 
  52 static ddi_dma_attr_t sfxge_mcdi_dma_attr = {
  53         DMA_ATTR_V0,            /* dma_attr_version     */
  54         0,                      /* dma_attr_addr_lo     */
  55         0xffffffffffffffffull,  /* dma_attr_addr_hi     */
  56         0xffffffffffffffffull,  /* dma_attr_count_max   */
  57         0x1000,                 /* dma_attr_align       */
  58         0xffffffff,             /* dma_attr_burstsizes  */
  59         1,                      /* dma_attr_minxfer     */
  60         0xffffffffffffffffull,  /* dma_attr_maxxfer     */
  61         0xffffffffffffffffull,  /* dma_attr_seg         */
  62         1,                      /* dma_attr_sgllen      */
  63         1,                      /* dma_attr_granular    */
  64         0                       /* dma_attr_flags       */
  65 };
  66 
  67 /*
  68  * Notes on MCDI operation:
  69  * ------------------------
  70  * MCDI requests can be made in arbitrary thread context, and as a synchronous
  71  * API must therefore block until the response is available from the MC, or
  72  * a watchdog timeout occurs.
  73  *
  74  * This interacts badly with the limited number of worker threads (2 per CPU)
  75  * used by the Solaris callout subsystem to invoke timeout handlers. If both
  76  * worker threads are blocked (e.g. waiting for a condvar or mutex) then timeout
  77  * processing is deadlocked on that CPU, causing system failure.
  78  *
  79  * For this reason the driver does not use event based MCDI completion, as this
  80  * leads to numerous paths involving timeouts and reentrant GLDv3 entrypoints
  81  * that result in a deadlocked system.
  82  */
  83 #define SFXGE_MCDI_POLL_INTERVAL        10              /* 10us in 1us units */
  84 #define SFXGE_MCDI_WATCHDOG_INTERVAL    10000000        /* 10s in 1us units */
  85 
  86 
  87 /* Acquire exclusive access to MCDI for the duration of a request */
  88 static void
  89 sfxge_mcdi_acquire(sfxge_mcdi_t *smp)
  90 {
  91         mutex_enter(&(smp->sm_lock));
  92         ASSERT3U(smp->sm_state, !=, SFXGE_MCDI_UNINITIALIZED);
  93 
  94         while (smp->sm_state != SFXGE_MCDI_INITIALIZED) {
  95                 (void) cv_wait(&(smp->sm_kv), &(smp->sm_lock));
  96         }
  97         smp->sm_state = SFXGE_MCDI_BUSY;
  98 
  99         mutex_exit(&(smp->sm_lock));
 100 }
 101 
 102 
 103 /* Release ownership of MCDI on request completion */
 104 static void
 105 sfxge_mcdi_release(sfxge_mcdi_t *smp)
 106 {
 107         mutex_enter(&(smp->sm_lock));
 108         ASSERT((smp->sm_state == SFXGE_MCDI_BUSY) ||
 109             (smp->sm_state == SFXGE_MCDI_COMPLETED));
 110 
 111         smp->sm_state = SFXGE_MCDI_INITIALIZED;
 112         cv_broadcast(&(smp->sm_kv));
 113 
 114         mutex_exit(&(smp->sm_lock));
 115 }
 116 
 117 
 118 static void
 119 sfxge_mcdi_timeout(sfxge_t *sp)
 120 {
 121         dev_info_t *dip = sp->s_dip;
 122 
 123         dev_err(dip, CE_WARN, SFXGE_CMN_ERR "MC_TIMEOUT");
 124 
 125         DTRACE_PROBE(mcdi_timeout);
 126         (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR,
 127             "MCDI timeout", 0);
 128 }
 129 
 130 
 131 static void
 132 sfxge_mcdi_poll(sfxge_t *sp)
 133 {
 134         efx_nic_t *enp = sp->s_enp;
 135         clock_t timeout;
 136         boolean_t aborted;
 137 
 138         /* Poll until request completes or timeout */
 139         timeout = ddi_get_lbolt() + drv_usectohz(SFXGE_MCDI_WATCHDOG_INTERVAL);
 140         while (efx_mcdi_request_poll(enp) == B_FALSE) {
 141 
 142                 /* No response received yet */
 143                 if (ddi_get_lbolt() > timeout) {
 144                         /* Timeout expired */
 145                         goto fail;
 146                 }
 147 
 148                 /* Short delay to avoid excessive PCIe traffic */
 149                 drv_usecwait(SFXGE_MCDI_POLL_INTERVAL);
 150         }
 151 
 152         /* Request completed (or polling failed) */
 153         return;
 154 
 155 fail:
 156         /* Timeout before request completion */
 157         DTRACE_PROBE(fail);
 158         aborted = efx_mcdi_request_abort(enp);
 159         ASSERT(aborted);
 160         sfxge_mcdi_timeout(sp);
 161 }
 162 
 163 
 164 static void
 165 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
 166 {
 167         sfxge_t *sp = (sfxge_t *)arg;
 168         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 169 
 170         sfxge_mcdi_acquire(smp);
 171 
 172         /* Issue request and poll for completion */
 173         efx_mcdi_request_start(sp->s_enp, emrp, B_FALSE);
 174         sfxge_mcdi_poll(sp);
 175 
 176         sfxge_mcdi_release(smp);
 177 }
 178 
 179 
 180 static void
 181 sfxge_mcdi_ev_cpl(void *arg)
 182 {
 183         sfxge_t *sp = (sfxge_t *)arg;
 184         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 185 
 186         mutex_enter(&(smp->sm_lock));
 187         ASSERT(smp->sm_state == SFXGE_MCDI_BUSY);
 188         smp->sm_state = SFXGE_MCDI_COMPLETED;
 189         cv_broadcast(&(smp->sm_kv));
 190         mutex_exit(&(smp->sm_lock));
 191 }
 192 
 193 
 194 static void
 195 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
 196 {
 197         sfxge_t *sp = (sfxge_t *)arg;
 198         const char *reason;
 199 
 200         if (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
 201                 reason = "MC_REBOOT";
 202         else if (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
 203                 reason = "MC_BADASSERT";
 204         else
 205                 reason = "MC_UNKNOWN";
 206 
 207         DTRACE_PROBE(mcdi_exception);
 208         /* sfxge_evq_t->se_lock held */
 209         (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR, reason, 0);
 210 }
 211 
 212 #if EFSYS_OPT_MCDI_LOGGING
 213 #define SFXGE_MCDI_LOG_BUF_SIZE 128
 214 
 215 static size_t
 216 sfxge_mcdi_do_log(char *buffer, void *data, size_t data_size,
 217                 size_t pfxsize, size_t position)
 218 {
 219         uint32_t *words = data;
 220         size_t i;
 221 
 222         for (i = 0; i < data_size; i += sizeof (*words)) {
 223                 if (position + 2 * sizeof (*words) + 1 >=
 224                     SFXGE_MCDI_LOG_BUF_SIZE) {
 225                         buffer[position] = '\0';
 226                         cmn_err(CE_NOTE, "%s \\", buffer);
 227                         position = pfxsize;
 228                 }
 229                 snprintf(buffer + position, SFXGE_MCDI_LOG_BUF_SIZE - position,
 230                     " %08x", *words);
 231                 words++;
 232                 position += 2 * sizeof (uint32_t) + 1;
 233         }
 234         return (position);
 235 }
 236 
 237 
 238 static void
 239 sfxge_mcdi_logger(void *arg, efx_log_msg_t type,
 240     void *header, size_t header_size, void *data, size_t data_size)
 241 {
 242         sfxge_t *sp = (sfxge_t *)arg;
 243         char buffer[SFXGE_MCDI_LOG_BUF_SIZE];
 244         size_t pfxsize;
 245         size_t start;
 246 
 247         if (!sp->s_mcdi_logging)
 248                 return;
 249 
 250         pfxsize = snprintf(buffer, sizeof (buffer),
 251             "sfc %04x:%02x:%02x.%02x %s%d MCDI RPC %s:",
 252             0,
 253             PCI_REG_BUS_G(sp->s_bus_addr),
 254             PCI_REG_DEV_G(sp->s_bus_addr),
 255             PCI_REG_FUNC_G(sp->s_bus_addr),
 256             ddi_driver_name(sp->s_dip),
 257             ddi_get_instance(sp->s_dip),
 258             type == EFX_LOG_MCDI_REQUEST ? "REQ" :
 259             type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
 260         start = sfxge_mcdi_do_log(buffer, header, header_size,
 261             pfxsize, pfxsize);
 262         start = sfxge_mcdi_do_log(buffer, data, data_size, pfxsize, start);
 263         if (start != pfxsize) {
 264                 buffer[start] = '\0';
 265                 cmn_err(CE_NOTE, "%s", buffer);
 266         }
 267 }
 268 #endif /* EFSYS_OPT_MCDI_LOGGING */
 269 
 270 int
 271 sfxge_mcdi_init(sfxge_t *sp)
 272 {
 273         efx_nic_t *enp = sp->s_enp;
 274         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 275         efsys_mem_t *esmp = &(smp->sm_mem);
 276         efx_mcdi_transport_t *emtp = &(smp->sm_emt);
 277         sfxge_dma_buffer_attr_t dma_attr;
 278         int msg_buf_size;
 279         int rc;
 280 
 281         ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_UNINITIALIZED);
 282 
 283         msg_buf_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
 284 
 285         /* Allocate host DMA buffer for MCDI commands */
 286         dma_attr.sdba_dip        = sp->s_dip;
 287         dma_attr.sdba_dattrp     = &sfxge_mcdi_dma_attr;
 288         dma_attr.sdba_callback   = DDI_DMA_SLEEP;
 289         dma_attr.sdba_length     = msg_buf_size;
 290         dma_attr.sdba_memflags   = DDI_DMA_CONSISTENT;
 291         dma_attr.sdba_devaccp    = &sfxge_mcdi_devacc;
 292         dma_attr.sdba_bindflags  = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
 293         dma_attr.sdba_maxcookies = 1;
 294         dma_attr.sdba_zeroinit   = B_TRUE;
 295 
 296         if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
 297                 goto fail1;
 298 
 299         mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER, NULL);
 300 
 301         smp->sm_state = SFXGE_MCDI_INITIALIZED;
 302 
 303         emtp->emt_context   = sp;
 304         emtp->emt_dma_mem   = esmp;
 305         emtp->emt_execute   = sfxge_mcdi_execute;
 306         emtp->emt_ev_cpl    = sfxge_mcdi_ev_cpl;
 307         emtp->emt_exception = sfxge_mcdi_exception;
 308 #if EFSYS_OPT_MCDI_LOGGING
 309         emtp->emt_logger    = sfxge_mcdi_logger;
 310 #endif
 311 
 312         cv_init(&(smp->sm_kv), NULL, CV_DRIVER, NULL);
 313 
 314         if ((rc = efx_mcdi_init(enp, emtp)) != 0)
 315                 goto fail2;
 316 
 317         return (0);
 318 
 319 fail2:
 320         DTRACE_PROBE(fail2);
 321 
 322         cv_destroy(&(smp->sm_kv));
 323         mutex_destroy(&(smp->sm_lock));
 324 
 325         sfxge_dma_buffer_destroy(esmp);
 326 
 327         smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
 328         smp->sm_sp = NULL;
 329         SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
 330 
 331 fail1:
 332         DTRACE_PROBE1(fail1, int, rc);
 333 
 334         return (rc);
 335 }
 336 
 337 
 338 void
 339 sfxge_mcdi_fini(sfxge_t *sp)
 340 {
 341         efx_nic_t *enp = sp->s_enp;
 342         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 343         efsys_mem_t *esmp = &(smp->sm_mem);
 344         efx_mcdi_transport_t *emtp;
 345 
 346         mutex_enter(&(smp->sm_lock));
 347         ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_INITIALIZED);
 348 
 349         efx_mcdi_fini(enp);
 350         emtp = &(smp->sm_emt);
 351         bzero(emtp, sizeof (*emtp));
 352 
 353         smp->sm_sp = NULL;
 354 
 355         cv_destroy(&(smp->sm_kv));
 356         mutex_exit(&(smp->sm_lock));
 357 
 358         sfxge_dma_buffer_destroy(esmp);
 359 
 360         mutex_destroy(&(smp->sm_lock));
 361 
 362         smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
 363         SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
 364 }
 365 
 366 
 367 int
 368 sfxge_mcdi_ioctl(sfxge_t *sp, sfxge_mcdi_ioc_t *smip)
 369 {
 370         const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
 371         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 372         efx_mcdi_req_t emr;
 373         uint8_t *out;
 374         int rc;
 375 
 376         if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
 377                 rc = ENODEV;
 378                 goto fail1;
 379         }
 380 
 381         if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
 382                 rc = ENOTSUP;
 383                 goto fail2;
 384         }
 385 
 386         out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
 387         if (out == NULL) {
 388                 rc = ENOMEM;
 389                 goto fail3;
 390         }
 391 
 392         emr.emr_cmd = smip->smi_cmd;
 393         emr.emr_in_buf = smip->smi_payload;
 394         emr.emr_in_length = smip->smi_len;
 395 
 396         emr.emr_out_buf = out;
 397         emr.emr_out_length = sizeof (smip->smi_payload);
 398 
 399         sfxge_mcdi_execute(sp, &emr);
 400 
 401         smip->smi_rc = (uint8_t)emr.emr_rc;
 402         smip->smi_cmd = (uint8_t)emr.emr_cmd;
 403         smip->smi_len = (uint8_t)emr.emr_out_length_used;
 404         bcopy(out, smip->smi_payload, smip->smi_len);
 405 
 406         /*
 407          * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
 408          * Both ports will see ->emt_exception callbacks on the next MCDI poll
 409          */
 410         if (smip->smi_cmd == MC_CMD_REBOOT) {
 411 
 412                 DTRACE_PROBE(mcdi_ioctl_mc_reboot);
 413                 /* sfxge_t->s_state_lock held */
 414                 (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
 415                     "MC_REBOOT triggering restart", 0);
 416         }
 417 
 418         kmem_free(out, sizeof (smip->smi_payload));
 419 
 420         return (0);
 421 
 422 fail3:
 423         DTRACE_PROBE(fail3);
 424 fail2:
 425         DTRACE_PROBE(fail2);
 426 fail1:
 427         DTRACE_PROBE1(fail1, int, rc);
 428         return (rc);
 429 }
 430 
 431 int
 432 sfxge_mcdi2_ioctl(sfxge_t *sp, sfxge_mcdi2_ioc_t *smip)
 433 {
 434         const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
 435         sfxge_mcdi_t *smp = &(sp->s_mcdi);
 436         efx_mcdi_req_t emr;
 437         uint8_t *out;
 438         int rc;
 439 
 440         if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
 441                 rc = ENODEV;
 442                 goto fail1;
 443         }
 444 
 445         if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
 446                 rc = ENOTSUP;
 447                 goto fail2;
 448         }
 449 
 450         out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
 451         if (out == NULL) {
 452                 rc = ENOMEM;
 453                 goto fail3;
 454         }
 455 
 456         emr.emr_cmd = smip->smi_cmd;
 457         emr.emr_in_buf = smip->smi_payload;
 458         emr.emr_in_length = smip->smi_len;
 459 
 460         emr.emr_out_buf = out;
 461         emr.emr_out_length = sizeof (smip->smi_payload);
 462 
 463         sfxge_mcdi_execute(sp, &emr);
 464 
 465         smip->smi_rc = emr.emr_rc;
 466         smip->smi_cmd = emr.emr_cmd;
 467         smip->smi_len = (uint32_t)emr.emr_out_length_used;
 468         bcopy(out, smip->smi_payload, smip->smi_len);
 469 
 470         /*
 471          * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
 472          * Both ports will see ->emt_exception callbacks on the next MCDI poll
 473          */
 474         if (smip->smi_cmd == MC_CMD_REBOOT) {
 475 
 476                 DTRACE_PROBE(mcdi_ioctl_mc_reboot);
 477                 /* sfxge_t->s_state_lock held */
 478                 (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
 479                     "MC_REBOOT triggering restart", 0);
 480         }
 481 
 482         kmem_free(out, sizeof (smip->smi_payload));
 483 
 484         return (0);
 485 
 486 fail3:
 487         DTRACE_PROBE(fail3);
 488 fail2:
 489         DTRACE_PROBE(fail2);
 490 fail1:
 491         DTRACE_PROBE1(fail1, int, rc);
 492         return (rc);
 493 }