1227569Sphilip/*- 2300607Sarybchik * Copyright (c) 2010-2016 Solarflare Communications Inc. 3227569Sphilip * All rights reserved. 4227569Sphilip * 5227569Sphilip * This software was developed in part by Philip Paeps under contract for 6227569Sphilip * Solarflare Communications, Inc. 7227569Sphilip * 8227569Sphilip * Redistribution and use in source and binary forms, with or without 9283514Sarybchik * modification, are permitted provided that the following conditions are met: 10227569Sphilip * 11283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice, 12283514Sarybchik * this list of conditions and the following disclaimer. 13283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice, 14283514Sarybchik * this list of conditions and the following disclaimer in the documentation 15283514Sarybchik * and/or other materials provided with the distribution. 16283514Sarybchik * 17283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 19283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28283514Sarybchik * 29283514Sarybchik * The views and conclusions contained in the software and documentation are 30283514Sarybchik * those of the authors and should not be interpreted as representing official 31283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project. 32227569Sphilip */ 33227569Sphilip 34227569Sphilip#include <sys/cdefs.h> 35227569Sphilip__FBSDID("$FreeBSD: stable/11/sys/dev/sfxge/sfxge_mcdi.c 311487 2017-01-06 07:20:20Z arybchik $"); 36227569Sphilip 37227569Sphilip#include <sys/param.h> 38227569Sphilip#include <sys/condvar.h> 39227569Sphilip#include <sys/lock.h> 40227569Sphilip#include <sys/mutex.h> 41227569Sphilip#include <sys/proc.h> 42227569Sphilip#include <sys/syslog.h> 43227569Sphilip#include <sys/taskqueue.h> 44283514Sarybchik#include <sys/malloc.h> 45227569Sphilip 46227569Sphilip#include "common/efx.h" 47227569Sphilip#include "common/efx_mcdi.h" 48227569Sphilip#include "common/efx_regs_mcdi.h" 49227569Sphilip 50227569Sphilip#include "sfxge.h" 51227569Sphilip 52291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING 53291843Sarybchik#include <dev/pci/pcivar.h> 54291843Sarybchik#endif 55291843Sarybchik 56227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ 57227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ 58227569Sphilip 59227569Sphilipstatic void 60227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc) 61227569Sphilip{ 62227569Sphilip device_t dev = sc->dev; 63227569Sphilip 64227569Sphilip log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), 65227569Sphilip device_get_unit(dev)); 66227569Sphilip 67227569Sphilip EFSYS_PROBE(mcdi_timeout); 68227569Sphilip sfxge_schedule_reset(sc); 69227569Sphilip} 70227569Sphilip 71227569Sphilipstatic void 72311487Sarybchiksfxge_mcdi_poll(struct sfxge_softc *sc, uint32_t timeout_us) 73227569Sphilip{ 74227569Sphilip efx_nic_t *enp; 75227569Sphilip clock_t delay_total; 76227569Sphilip clock_t delay_us; 77227569Sphilip boolean_t aborted; 78227569Sphilip 79227569Sphilip delay_total = 0; 80227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN; 81227569Sphilip enp = sc->enp; 82227569Sphilip 83227569Sphilip do { 84227569Sphilip if (efx_mcdi_request_poll(enp)) { 85227569Sphilip EFSYS_PROBE1(mcdi_delay, clock_t, delay_total); 86227569Sphilip return; 87227569Sphilip } 88227569Sphilip 89311487Sarybchik if (delay_total > timeout_us) { 90227569Sphilip aborted = efx_mcdi_request_abort(enp); 91227569Sphilip KASSERT(aborted, ("abort failed")); 92227569Sphilip sfxge_mcdi_timeout(sc); 93227569Sphilip return; 94227569Sphilip } 95227569Sphilip 96227569Sphilip /* Spin or block depending on delay interval. */ 97227569Sphilip if (delay_us < 1000000) 98227569Sphilip DELAY(delay_us); 99227569Sphilip else 100227569Sphilip pause("mcdi wait", delay_us * hz / 1000000); 101227569Sphilip 102227569Sphilip delay_total += delay_us; 103227569Sphilip 104227569Sphilip /* Exponentially back off the poll frequency. */ 105227569Sphilip delay_us = delay_us * 2; 106227569Sphilip if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX) 107227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX; 108227569Sphilip 109227569Sphilip } while (1); 110227569Sphilip} 111227569Sphilip 112227569Sphilipstatic void 113227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 114227569Sphilip{ 115227569Sphilip struct sfxge_softc *sc; 116227569Sphilip struct sfxge_mcdi *mcdi; 117311487Sarybchik uint32_t timeout_us = 0; 118227569Sphilip 119227569Sphilip sc = (struct sfxge_softc *)arg; 120227569Sphilip mcdi = &sc->mcdi; 121227569Sphilip 122283514Sarybchik SFXGE_MCDI_LOCK(mcdi); 123227569Sphilip 124283514Sarybchik KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 125283514Sarybchik ("MCDI not initialized")); 126283514Sarybchik 127227569Sphilip /* Issue request and poll for completion. */ 128311487Sarybchik efx_mcdi_get_timeout(sc->enp, emrp, &timeout_us); 129311487Sarybchik KASSERT(timeout_us > 0, ("MCDI timeout not initialized")); 130311487Sarybchik 131227569Sphilip efx_mcdi_request_start(sc->enp, emrp, B_FALSE); 132311487Sarybchik sfxge_mcdi_poll(sc, timeout_us); 133227569Sphilip 134283514Sarybchik SFXGE_MCDI_UNLOCK(mcdi); 135227569Sphilip} 136227569Sphilip 137227569Sphilipstatic void 138227569Sphilipsfxge_mcdi_ev_cpl(void *arg) 139227569Sphilip{ 140227569Sphilip struct sfxge_softc *sc; 141227569Sphilip struct sfxge_mcdi *mcdi; 142227569Sphilip 143227569Sphilip sc = (struct sfxge_softc *)arg; 144227569Sphilip mcdi = &sc->mcdi; 145227569Sphilip 146283514Sarybchik KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 147283514Sarybchik ("MCDI not initialized")); 148283514Sarybchik 149283514Sarybchik /* We do not use MCDI completion, MCDI is simply polled */ 150227569Sphilip} 151227569Sphilip 152227569Sphilipstatic void 153227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 154227569Sphilip{ 155227569Sphilip struct sfxge_softc *sc; 156227569Sphilip device_t dev; 157227569Sphilip 158227569Sphilip sc = (struct sfxge_softc *)arg; 159227569Sphilip dev = sc->dev; 160227569Sphilip 161227569Sphilip log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), 162227569Sphilip device_get_unit(dev), 163227569Sphilip (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) 164227569Sphilip ? "REBOOT" 165227569Sphilip : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) 166227569Sphilip ? "BADASSERT" : "UNKNOWN"); 167227569Sphilip 168227569Sphilip EFSYS_PROBE(mcdi_exception); 169227569Sphilip 170227569Sphilip sfxge_schedule_reset(sc); 171227569Sphilip} 172227569Sphilip 173291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING 174291843Sarybchik 175291843Sarybchik#define SFXGE_MCDI_LOG_BUF_SIZE 128 176291843Sarybchik 177291843Sarybchikstatic size_t 178291843Sarybchiksfxge_mcdi_do_log(char *buffer, void *data, size_t data_size, 179291843Sarybchik size_t pfxsize, size_t position) 180291843Sarybchik{ 181291843Sarybchik uint32_t *words = data; 182291843Sarybchik size_t i; 183291843Sarybchik 184291843Sarybchik for (i = 0; i < data_size; i += sizeof(*words)) { 185291843Sarybchik if (position + 2 * sizeof(*words) + 1 >= SFXGE_MCDI_LOG_BUF_SIZE) { 186291843Sarybchik buffer[position] = '\0'; 187291843Sarybchik printf("%s \\\n", buffer); 188291843Sarybchik position = pfxsize; 189291843Sarybchik } 190291843Sarybchik snprintf(buffer + position, SFXGE_MCDI_LOG_BUF_SIZE - position, 191291843Sarybchik " %08x", *words); 192291843Sarybchik words++; 193291843Sarybchik position += 2 * sizeof(uint32_t) + 1; 194291843Sarybchik } 195291843Sarybchik return (position); 196291843Sarybchik} 197291843Sarybchik 198291843Sarybchikstatic void 199291843Sarybchiksfxge_mcdi_logger(void *arg, efx_log_msg_t type, 200291843Sarybchik void *header, size_t header_size, 201291843Sarybchik void *data, size_t data_size) 202291843Sarybchik{ 203291843Sarybchik struct sfxge_softc *sc = (struct sfxge_softc *)arg; 204291843Sarybchik char buffer[SFXGE_MCDI_LOG_BUF_SIZE]; 205291843Sarybchik size_t pfxsize; 206291843Sarybchik size_t start; 207291843Sarybchik 208291843Sarybchik if (!sc->mcdi_logging) 209291843Sarybchik return; 210291843Sarybchik 211291843Sarybchik pfxsize = snprintf(buffer, sizeof(buffer), 212291843Sarybchik "sfc %04x:%02x:%02x.%02x %s MCDI RPC %s:", 213291843Sarybchik pci_get_domain(sc->dev), 214291843Sarybchik pci_get_bus(sc->dev), 215291843Sarybchik pci_get_slot(sc->dev), 216291843Sarybchik pci_get_function(sc->dev), 217291843Sarybchik device_get_nameunit(sc->dev), 218291843Sarybchik type == EFX_LOG_MCDI_REQUEST ? "REQ" : 219291843Sarybchik type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???"); 220291843Sarybchik start = sfxge_mcdi_do_log(buffer, header, header_size, 221291843Sarybchik pfxsize, pfxsize); 222291843Sarybchik start = sfxge_mcdi_do_log(buffer, data, data_size, pfxsize, start); 223291843Sarybchik if (start != pfxsize) { 224291843Sarybchik buffer[start] = '\0'; 225291843Sarybchik printf("%s\n", buffer); 226291843Sarybchik } 227291843Sarybchik} 228291843Sarybchik 229291843Sarybchik#endif 230291843Sarybchik 231227569Sphilipint 232283514Sarybchiksfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) 233283514Sarybchik{ 234283514Sarybchik const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); 235283514Sarybchik struct sfxge_mcdi *mp = &(sc->mcdi); 236283514Sarybchik efx_mcdi_req_t emr; 237283514Sarybchik uint8_t *mcdibuf; 238283514Sarybchik int rc; 239283514Sarybchik 240283514Sarybchik if (mp->state == SFXGE_MCDI_UNINITIALIZED) { 241283514Sarybchik rc = ENODEV; 242283514Sarybchik goto fail1; 243283514Sarybchik } 244283514Sarybchik 245283514Sarybchik if (!(encp->enc_features & EFX_FEATURE_MCDI)) { 246283514Sarybchik rc = ENOTSUP; 247283514Sarybchik goto fail2; 248283514Sarybchik } 249283514Sarybchik 250283514Sarybchik if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) { 251283514Sarybchik rc = EINVAL; 252283514Sarybchik goto fail3; 253283514Sarybchik } 254283514Sarybchik 255283514Sarybchik mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO); 256283514Sarybchik if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) { 257283514Sarybchik goto fail5; 258283514Sarybchik } 259283514Sarybchik 260283514Sarybchik emr.emr_cmd = ip->u.mcdi.cmd; 261283514Sarybchik emr.emr_in_buf = mcdibuf; 262283514Sarybchik emr.emr_in_length = ip->u.mcdi.len; 263283514Sarybchik 264283514Sarybchik emr.emr_out_buf = mcdibuf; 265283514Sarybchik emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD; 266283514Sarybchik 267283514Sarybchik sfxge_mcdi_execute(sc, &emr); 268283514Sarybchik 269283514Sarybchik ip->u.mcdi.rc = emr.emr_rc; 270283514Sarybchik ip->u.mcdi.cmd = emr.emr_cmd; 271283514Sarybchik ip->u.mcdi.len = emr.emr_out_length_used; 272283514Sarybchik if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) { 273283514Sarybchik goto fail6; 274283514Sarybchik } 275283514Sarybchik 276283514Sarybchik /* 277283514Sarybchik * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT 278283514Sarybchik * Both ports will see ->emt_exception callbacks on the next MCDI poll 279283514Sarybchik */ 280283514Sarybchik if (ip->u.mcdi.cmd == MC_CMD_REBOOT) { 281283514Sarybchik 282283514Sarybchik EFSYS_PROBE(mcdi_ioctl_mc_reboot); 283283514Sarybchik /* sfxge_t->s_state_lock held */ 284283514Sarybchik (void) sfxge_schedule_reset(sc); 285283514Sarybchik } 286283514Sarybchik 287283514Sarybchik free(mcdibuf, M_TEMP); 288283514Sarybchik 289283514Sarybchik return (0); 290283514Sarybchik 291283514Sarybchikfail6: 292283514Sarybchikfail5: 293283514Sarybchik free(mcdibuf, M_TEMP); 294283514Sarybchikfail3: 295283514Sarybchikfail2: 296283514Sarybchikfail1: 297283514Sarybchik return (rc); 298283514Sarybchik} 299283514Sarybchik 300283514Sarybchik 301283514Sarybchikint 302227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc) 303227569Sphilip{ 304227569Sphilip efx_nic_t *enp; 305227569Sphilip struct sfxge_mcdi *mcdi; 306227569Sphilip efx_mcdi_transport_t *emtp; 307283514Sarybchik efsys_mem_t *esmp; 308283514Sarybchik int max_msg_size; 309227569Sphilip int rc; 310227569Sphilip 311227569Sphilip enp = sc->enp; 312227569Sphilip mcdi = &sc->mcdi; 313227569Sphilip emtp = &mcdi->transport; 314283514Sarybchik esmp = &mcdi->mem; 315283514Sarybchik max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 316227569Sphilip 317227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, 318227569Sphilip ("MCDI already initialized")); 319227569Sphilip 320278250Sarybchik SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev)); 321227569Sphilip 322227569Sphilip mcdi->state = SFXGE_MCDI_INITIALIZED; 323227569Sphilip 324283514Sarybchik if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0) 325283514Sarybchik goto fail; 326283514Sarybchik 327227569Sphilip emtp->emt_context = sc; 328283514Sarybchik emtp->emt_dma_mem = esmp; 329227569Sphilip emtp->emt_execute = sfxge_mcdi_execute; 330227569Sphilip emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; 331227569Sphilip emtp->emt_exception = sfxge_mcdi_exception; 332291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING 333291843Sarybchik emtp->emt_logger = sfxge_mcdi_logger; 334291843Sarybchik SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), 335291843Sarybchik SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 336291843Sarybchik OID_AUTO, "mcdi_logging", CTLFLAG_RW, 337291843Sarybchik &sc->mcdi_logging, 0, 338291843Sarybchik "MCDI logging"); 339291843Sarybchik#endif 340227569Sphilip 341227569Sphilip if ((rc = efx_mcdi_init(enp, emtp)) != 0) 342227569Sphilip goto fail; 343227569Sphilip 344227569Sphilip return (0); 345227569Sphilip 346227569Sphilipfail: 347278221Sarybchik SFXGE_MCDI_LOCK_DESTROY(mcdi); 348227569Sphilip mcdi->state = SFXGE_MCDI_UNINITIALIZED; 349227569Sphilip return (rc); 350227569Sphilip} 351227569Sphilip 352227569Sphilipvoid 353227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc) 354227569Sphilip{ 355227569Sphilip struct sfxge_mcdi *mcdi; 356227569Sphilip efx_nic_t *enp; 357227569Sphilip efx_mcdi_transport_t *emtp; 358283514Sarybchik efsys_mem_t *esmp; 359227569Sphilip 360227569Sphilip enp = sc->enp; 361227569Sphilip mcdi = &sc->mcdi; 362227569Sphilip emtp = &mcdi->transport; 363283514Sarybchik esmp = &mcdi->mem; 364227569Sphilip 365278221Sarybchik SFXGE_MCDI_LOCK(mcdi); 366227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 367227569Sphilip ("MCDI not initialized")); 368227569Sphilip 369227569Sphilip efx_mcdi_fini(enp); 370227569Sphilip bzero(emtp, sizeof(*emtp)); 371227569Sphilip 372278221Sarybchik SFXGE_MCDI_UNLOCK(mcdi); 373227569Sphilip 374283514Sarybchik sfxge_dma_free(esmp); 375283514Sarybchik 376278221Sarybchik SFXGE_MCDI_LOCK_DESTROY(mcdi); 377227569Sphilip} 378