sfxge_mcdi.c revision 283514
1227569Sphilip/*- 2283514Sarybchik * Copyright (c) 2010-2015 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: head/sys/dev/sfxge/sfxge_mcdi.c 283514 2015-05-25 08:34:55Z 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 52227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ 53227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ 54227569Sphilip#define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ 55227569Sphilip 56227569Sphilipstatic void 57227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc) 58227569Sphilip{ 59227569Sphilip device_t dev = sc->dev; 60227569Sphilip 61227569Sphilip log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), 62227569Sphilip device_get_unit(dev)); 63227569Sphilip 64227569Sphilip EFSYS_PROBE(mcdi_timeout); 65227569Sphilip sfxge_schedule_reset(sc); 66227569Sphilip} 67227569Sphilip 68227569Sphilipstatic void 69227569Sphilipsfxge_mcdi_poll(struct sfxge_softc *sc) 70227569Sphilip{ 71227569Sphilip efx_nic_t *enp; 72227569Sphilip clock_t delay_total; 73227569Sphilip clock_t delay_us; 74227569Sphilip boolean_t aborted; 75227569Sphilip 76227569Sphilip delay_total = 0; 77227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN; 78227569Sphilip enp = sc->enp; 79227569Sphilip 80227569Sphilip do { 81227569Sphilip if (efx_mcdi_request_poll(enp)) { 82227569Sphilip EFSYS_PROBE1(mcdi_delay, clock_t, delay_total); 83227569Sphilip return; 84227569Sphilip } 85227569Sphilip 86227569Sphilip if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) { 87227569Sphilip aborted = efx_mcdi_request_abort(enp); 88227569Sphilip KASSERT(aborted, ("abort failed")); 89227569Sphilip sfxge_mcdi_timeout(sc); 90227569Sphilip return; 91227569Sphilip } 92227569Sphilip 93227569Sphilip /* Spin or block depending on delay interval. */ 94227569Sphilip if (delay_us < 1000000) 95227569Sphilip DELAY(delay_us); 96227569Sphilip else 97227569Sphilip pause("mcdi wait", delay_us * hz / 1000000); 98227569Sphilip 99227569Sphilip delay_total += delay_us; 100227569Sphilip 101227569Sphilip /* Exponentially back off the poll frequency. */ 102227569Sphilip delay_us = delay_us * 2; 103227569Sphilip if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX) 104227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX; 105227569Sphilip 106227569Sphilip } while (1); 107227569Sphilip} 108227569Sphilip 109227569Sphilipstatic void 110227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 111227569Sphilip{ 112227569Sphilip struct sfxge_softc *sc; 113227569Sphilip struct sfxge_mcdi *mcdi; 114227569Sphilip 115227569Sphilip sc = (struct sfxge_softc *)arg; 116227569Sphilip mcdi = &sc->mcdi; 117227569Sphilip 118283514Sarybchik SFXGE_MCDI_LOCK(mcdi); 119227569Sphilip 120283514Sarybchik KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 121283514Sarybchik ("MCDI not initialized")); 122283514Sarybchik 123227569Sphilip /* Issue request and poll for completion. */ 124227569Sphilip efx_mcdi_request_start(sc->enp, emrp, B_FALSE); 125227569Sphilip sfxge_mcdi_poll(sc); 126227569Sphilip 127283514Sarybchik SFXGE_MCDI_UNLOCK(mcdi); 128227569Sphilip} 129227569Sphilip 130227569Sphilipstatic void 131227569Sphilipsfxge_mcdi_ev_cpl(void *arg) 132227569Sphilip{ 133227569Sphilip struct sfxge_softc *sc; 134227569Sphilip struct sfxge_mcdi *mcdi; 135227569Sphilip 136227569Sphilip sc = (struct sfxge_softc *)arg; 137227569Sphilip mcdi = &sc->mcdi; 138227569Sphilip 139283514Sarybchik KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 140283514Sarybchik ("MCDI not initialized")); 141283514Sarybchik 142283514Sarybchik /* We do not use MCDI completion, MCDI is simply polled */ 143227569Sphilip} 144227569Sphilip 145227569Sphilipstatic void 146227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 147227569Sphilip{ 148227569Sphilip struct sfxge_softc *sc; 149227569Sphilip device_t dev; 150227569Sphilip 151227569Sphilip sc = (struct sfxge_softc *)arg; 152227569Sphilip dev = sc->dev; 153227569Sphilip 154227569Sphilip log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), 155227569Sphilip device_get_unit(dev), 156227569Sphilip (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) 157227569Sphilip ? "REBOOT" 158227569Sphilip : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) 159227569Sphilip ? "BADASSERT" : "UNKNOWN"); 160227569Sphilip 161227569Sphilip EFSYS_PROBE(mcdi_exception); 162227569Sphilip 163227569Sphilip sfxge_schedule_reset(sc); 164227569Sphilip} 165227569Sphilip 166227569Sphilipint 167283514Sarybchiksfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) 168283514Sarybchik{ 169283514Sarybchik const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); 170283514Sarybchik struct sfxge_mcdi *mp = &(sc->mcdi); 171283514Sarybchik efx_mcdi_req_t emr; 172283514Sarybchik uint8_t *mcdibuf; 173283514Sarybchik int rc; 174283514Sarybchik 175283514Sarybchik if (mp->state == SFXGE_MCDI_UNINITIALIZED) { 176283514Sarybchik rc = ENODEV; 177283514Sarybchik goto fail1; 178283514Sarybchik } 179283514Sarybchik 180283514Sarybchik if (!(encp->enc_features & EFX_FEATURE_MCDI)) { 181283514Sarybchik rc = ENOTSUP; 182283514Sarybchik goto fail2; 183283514Sarybchik } 184283514Sarybchik 185283514Sarybchik if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) { 186283514Sarybchik rc = EINVAL; 187283514Sarybchik goto fail3; 188283514Sarybchik } 189283514Sarybchik 190283514Sarybchik mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO); 191283514Sarybchik if (mcdibuf == NULL) { 192283514Sarybchik rc = ENOMEM; 193283514Sarybchik goto fail4; 194283514Sarybchik } 195283514Sarybchik if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) { 196283514Sarybchik goto fail5; 197283514Sarybchik } 198283514Sarybchik 199283514Sarybchik emr.emr_cmd = ip->u.mcdi.cmd; 200283514Sarybchik emr.emr_in_buf = mcdibuf; 201283514Sarybchik emr.emr_in_length = ip->u.mcdi.len; 202283514Sarybchik 203283514Sarybchik emr.emr_out_buf = mcdibuf; 204283514Sarybchik emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD; 205283514Sarybchik 206283514Sarybchik sfxge_mcdi_execute(sc, &emr); 207283514Sarybchik 208283514Sarybchik ip->u.mcdi.rc = emr.emr_rc; 209283514Sarybchik ip->u.mcdi.cmd = emr.emr_cmd; 210283514Sarybchik ip->u.mcdi.len = emr.emr_out_length_used; 211283514Sarybchik if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) { 212283514Sarybchik goto fail6; 213283514Sarybchik } 214283514Sarybchik 215283514Sarybchik /* 216283514Sarybchik * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT 217283514Sarybchik * Both ports will see ->emt_exception callbacks on the next MCDI poll 218283514Sarybchik */ 219283514Sarybchik if (ip->u.mcdi.cmd == MC_CMD_REBOOT) { 220283514Sarybchik 221283514Sarybchik EFSYS_PROBE(mcdi_ioctl_mc_reboot); 222283514Sarybchik /* sfxge_t->s_state_lock held */ 223283514Sarybchik (void) sfxge_schedule_reset(sc); 224283514Sarybchik } 225283514Sarybchik 226283514Sarybchik free(mcdibuf, M_TEMP); 227283514Sarybchik 228283514Sarybchik return (0); 229283514Sarybchik 230283514Sarybchikfail6: 231283514Sarybchikfail5: 232283514Sarybchik free(mcdibuf, M_TEMP); 233283514Sarybchikfail4: 234283514Sarybchikfail3: 235283514Sarybchikfail2: 236283514Sarybchikfail1: 237283514Sarybchik return (rc); 238283514Sarybchik} 239283514Sarybchik 240283514Sarybchik 241283514Sarybchikint 242227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc) 243227569Sphilip{ 244227569Sphilip efx_nic_t *enp; 245227569Sphilip struct sfxge_mcdi *mcdi; 246227569Sphilip efx_mcdi_transport_t *emtp; 247283514Sarybchik efsys_mem_t *esmp; 248283514Sarybchik int max_msg_size; 249227569Sphilip int rc; 250227569Sphilip 251227569Sphilip enp = sc->enp; 252227569Sphilip mcdi = &sc->mcdi; 253227569Sphilip emtp = &mcdi->transport; 254283514Sarybchik esmp = &mcdi->mem; 255283514Sarybchik max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 256227569Sphilip 257227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, 258227569Sphilip ("MCDI already initialized")); 259227569Sphilip 260278250Sarybchik SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev)); 261227569Sphilip 262227569Sphilip mcdi->state = SFXGE_MCDI_INITIALIZED; 263227569Sphilip 264283514Sarybchik if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0) 265283514Sarybchik goto fail; 266283514Sarybchik 267227569Sphilip emtp->emt_context = sc; 268283514Sarybchik emtp->emt_dma_mem = esmp; 269227569Sphilip emtp->emt_execute = sfxge_mcdi_execute; 270227569Sphilip emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; 271227569Sphilip emtp->emt_exception = sfxge_mcdi_exception; 272227569Sphilip 273227569Sphilip if ((rc = efx_mcdi_init(enp, emtp)) != 0) 274227569Sphilip goto fail; 275227569Sphilip 276227569Sphilip return (0); 277227569Sphilip 278227569Sphilipfail: 279278221Sarybchik SFXGE_MCDI_LOCK_DESTROY(mcdi); 280227569Sphilip mcdi->state = SFXGE_MCDI_UNINITIALIZED; 281227569Sphilip return (rc); 282227569Sphilip} 283227569Sphilip 284227569Sphilipvoid 285227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc) 286227569Sphilip{ 287227569Sphilip struct sfxge_mcdi *mcdi; 288227569Sphilip efx_nic_t *enp; 289227569Sphilip efx_mcdi_transport_t *emtp; 290283514Sarybchik efsys_mem_t *esmp; 291227569Sphilip 292227569Sphilip enp = sc->enp; 293227569Sphilip mcdi = &sc->mcdi; 294227569Sphilip emtp = &mcdi->transport; 295283514Sarybchik esmp = &mcdi->mem; 296227569Sphilip 297278221Sarybchik SFXGE_MCDI_LOCK(mcdi); 298227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 299227569Sphilip ("MCDI not initialized")); 300227569Sphilip 301227569Sphilip efx_mcdi_fini(enp); 302227569Sphilip bzero(emtp, sizeof(*emtp)); 303227569Sphilip 304278221Sarybchik SFXGE_MCDI_UNLOCK(mcdi); 305227569Sphilip 306283514Sarybchik sfxge_dma_free(esmp); 307283514Sarybchik 308278221Sarybchik SFXGE_MCDI_LOCK_DESTROY(mcdi); 309227569Sphilip} 310