sfxge_mcdi.c revision 227569
1227569Sphilip/*- 2227569Sphilip * Copyright (c) 2010-2011 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 9227569Sphilip * modification, are permitted provided that the following conditions 10227569Sphilip * are met: 11227569Sphilip * 1. Redistributions of source code must retain the above copyright 12227569Sphilip * notice, this list of conditions and the following disclaimer. 13227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright 14227569Sphilip * notice, this list of conditions and the following disclaimer in the 15227569Sphilip * documentation and/or other materials provided with the distribution. 16227569Sphilip * 17227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20227569Sphilip * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27227569Sphilip * SUCH DAMAGE. 28227569Sphilip */ 29227569Sphilip 30227569Sphilip#include <sys/cdefs.h> 31227569Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/sfxge_mcdi.c 227569 2011-11-16 17:11:13Z philip $"); 32227569Sphilip 33227569Sphilip#include <sys/param.h> 34227569Sphilip#include <sys/condvar.h> 35227569Sphilip#include <sys/lock.h> 36227569Sphilip#include <sys/mutex.h> 37227569Sphilip#include <sys/proc.h> 38227569Sphilip#include <sys/syslog.h> 39227569Sphilip#include <sys/taskqueue.h> 40227569Sphilip 41227569Sphilip#include "common/efx.h" 42227569Sphilip#include "common/efx_mcdi.h" 43227569Sphilip#include "common/efx_regs_mcdi.h" 44227569Sphilip 45227569Sphilip#include "sfxge.h" 46227569Sphilip 47227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ 48227569Sphilip#define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ 49227569Sphilip#define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ 50227569Sphilip 51227569Sphilip/* Acquire exclusive access to MCDI for the duration of a request. */ 52227569Sphilipstatic void 53227569Sphilipsfxge_mcdi_acquire(struct sfxge_mcdi *mcdi) 54227569Sphilip{ 55227569Sphilip 56227569Sphilip mtx_lock(&mcdi->lock); 57227569Sphilip KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED, 58227569Sphilip ("MCDI not initialized")); 59227569Sphilip 60227569Sphilip while (mcdi->state != SFXGE_MCDI_INITIALIZED) 61227569Sphilip (void)cv_wait_sig(&mcdi->cv, &mcdi->lock); 62227569Sphilip mcdi->state = SFXGE_MCDI_BUSY; 63227569Sphilip 64227569Sphilip mtx_unlock(&mcdi->lock); 65227569Sphilip} 66227569Sphilip 67227569Sphilip/* Release ownership of MCDI on request completion. */ 68227569Sphilipstatic void 69227569Sphilipsfxge_mcdi_release(struct sfxge_mcdi *mcdi) 70227569Sphilip{ 71227569Sphilip 72227569Sphilip mtx_lock(&mcdi->lock); 73227569Sphilip KASSERT((mcdi->state == SFXGE_MCDI_BUSY || 74227569Sphilip mcdi->state == SFXGE_MCDI_COMPLETED), 75227569Sphilip ("MCDI not busy or task not completed")); 76227569Sphilip 77227569Sphilip mcdi->state = SFXGE_MCDI_INITIALIZED; 78227569Sphilip cv_broadcast(&mcdi->cv); 79227569Sphilip 80227569Sphilip mtx_unlock(&mcdi->lock); 81227569Sphilip} 82227569Sphilip 83227569Sphilipstatic void 84227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc) 85227569Sphilip{ 86227569Sphilip device_t dev = sc->dev; 87227569Sphilip 88227569Sphilip log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), 89227569Sphilip device_get_unit(dev)); 90227569Sphilip 91227569Sphilip EFSYS_PROBE(mcdi_timeout); 92227569Sphilip sfxge_schedule_reset(sc); 93227569Sphilip} 94227569Sphilip 95227569Sphilipstatic void 96227569Sphilipsfxge_mcdi_poll(struct sfxge_softc *sc) 97227569Sphilip{ 98227569Sphilip efx_nic_t *enp; 99227569Sphilip clock_t delay_total; 100227569Sphilip clock_t delay_us; 101227569Sphilip boolean_t aborted; 102227569Sphilip 103227569Sphilip delay_total = 0; 104227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN; 105227569Sphilip enp = sc->enp; 106227569Sphilip 107227569Sphilip do { 108227569Sphilip if (efx_mcdi_request_poll(enp)) { 109227569Sphilip EFSYS_PROBE1(mcdi_delay, clock_t, delay_total); 110227569Sphilip return; 111227569Sphilip } 112227569Sphilip 113227569Sphilip if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) { 114227569Sphilip aborted = efx_mcdi_request_abort(enp); 115227569Sphilip KASSERT(aborted, ("abort failed")); 116227569Sphilip sfxge_mcdi_timeout(sc); 117227569Sphilip return; 118227569Sphilip } 119227569Sphilip 120227569Sphilip /* Spin or block depending on delay interval. */ 121227569Sphilip if (delay_us < 1000000) 122227569Sphilip DELAY(delay_us); 123227569Sphilip else 124227569Sphilip pause("mcdi wait", delay_us * hz / 1000000); 125227569Sphilip 126227569Sphilip delay_total += delay_us; 127227569Sphilip 128227569Sphilip /* Exponentially back off the poll frequency. */ 129227569Sphilip delay_us = delay_us * 2; 130227569Sphilip if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX) 131227569Sphilip delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX; 132227569Sphilip 133227569Sphilip } while (1); 134227569Sphilip} 135227569Sphilip 136227569Sphilipstatic void 137227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 138227569Sphilip{ 139227569Sphilip struct sfxge_softc *sc; 140227569Sphilip struct sfxge_mcdi *mcdi; 141227569Sphilip 142227569Sphilip sc = (struct sfxge_softc *)arg; 143227569Sphilip mcdi = &sc->mcdi; 144227569Sphilip 145227569Sphilip sfxge_mcdi_acquire(mcdi); 146227569Sphilip 147227569Sphilip /* Issue request and poll for completion. */ 148227569Sphilip efx_mcdi_request_start(sc->enp, emrp, B_FALSE); 149227569Sphilip sfxge_mcdi_poll(sc); 150227569Sphilip 151227569Sphilip sfxge_mcdi_release(mcdi); 152227569Sphilip} 153227569Sphilip 154227569Sphilipstatic void 155227569Sphilipsfxge_mcdi_ev_cpl(void *arg) 156227569Sphilip{ 157227569Sphilip struct sfxge_softc *sc; 158227569Sphilip struct sfxge_mcdi *mcdi; 159227569Sphilip 160227569Sphilip sc = (struct sfxge_softc *)arg; 161227569Sphilip mcdi = &sc->mcdi; 162227569Sphilip 163227569Sphilip mtx_lock(&mcdi->lock); 164227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy")); 165227569Sphilip mcdi->state = SFXGE_MCDI_COMPLETED; 166227569Sphilip cv_broadcast(&mcdi->cv); 167227569Sphilip mtx_unlock(&mcdi->lock); 168227569Sphilip} 169227569Sphilip 170227569Sphilipstatic void 171227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 172227569Sphilip{ 173227569Sphilip struct sfxge_softc *sc; 174227569Sphilip device_t dev; 175227569Sphilip 176227569Sphilip sc = (struct sfxge_softc *)arg; 177227569Sphilip dev = sc->dev; 178227569Sphilip 179227569Sphilip log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), 180227569Sphilip device_get_unit(dev), 181227569Sphilip (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) 182227569Sphilip ? "REBOOT" 183227569Sphilip : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) 184227569Sphilip ? "BADASSERT" : "UNKNOWN"); 185227569Sphilip 186227569Sphilip EFSYS_PROBE(mcdi_exception); 187227569Sphilip 188227569Sphilip sfxge_schedule_reset(sc); 189227569Sphilip} 190227569Sphilip 191227569Sphilipint 192227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc) 193227569Sphilip{ 194227569Sphilip efx_nic_t *enp; 195227569Sphilip struct sfxge_mcdi *mcdi; 196227569Sphilip efx_mcdi_transport_t *emtp; 197227569Sphilip int rc; 198227569Sphilip 199227569Sphilip enp = sc->enp; 200227569Sphilip mcdi = &sc->mcdi; 201227569Sphilip emtp = &mcdi->transport; 202227569Sphilip 203227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, 204227569Sphilip ("MCDI already initialized")); 205227569Sphilip 206227569Sphilip mtx_init(&mcdi->lock, "sfxge_mcdi", NULL, MTX_DEF); 207227569Sphilip 208227569Sphilip mcdi->state = SFXGE_MCDI_INITIALIZED; 209227569Sphilip 210227569Sphilip emtp->emt_context = sc; 211227569Sphilip emtp->emt_execute = sfxge_mcdi_execute; 212227569Sphilip emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; 213227569Sphilip emtp->emt_exception = sfxge_mcdi_exception; 214227569Sphilip 215227569Sphilip cv_init(&mcdi->cv, "sfxge_mcdi"); 216227569Sphilip 217227569Sphilip if ((rc = efx_mcdi_init(enp, emtp)) != 0) 218227569Sphilip goto fail; 219227569Sphilip 220227569Sphilip return (0); 221227569Sphilip 222227569Sphilipfail: 223227569Sphilip mtx_destroy(&mcdi->lock); 224227569Sphilip mcdi->state = SFXGE_MCDI_UNINITIALIZED; 225227569Sphilip return (rc); 226227569Sphilip} 227227569Sphilip 228227569Sphilipvoid 229227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc) 230227569Sphilip{ 231227569Sphilip struct sfxge_mcdi *mcdi; 232227569Sphilip efx_nic_t *enp; 233227569Sphilip efx_mcdi_transport_t *emtp; 234227569Sphilip 235227569Sphilip enp = sc->enp; 236227569Sphilip mcdi = &sc->mcdi; 237227569Sphilip emtp = &mcdi->transport; 238227569Sphilip 239227569Sphilip mtx_lock(&mcdi->lock); 240227569Sphilip KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 241227569Sphilip ("MCDI not initialized")); 242227569Sphilip 243227569Sphilip efx_mcdi_fini(enp); 244227569Sphilip bzero(emtp, sizeof(*emtp)); 245227569Sphilip 246227569Sphilip cv_destroy(&mcdi->cv); 247227569Sphilip mtx_unlock(&mcdi->lock); 248227569Sphilip 249227569Sphilip mtx_destroy(&mcdi->lock); 250227569Sphilip} 251