Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/sfxge/sfxge_mcdi.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | /*- | ||||
* Copyright (c) 2010-2011 Solarflare Communications, Inc. | * Copyright (c) 2010-2015 Solarflare Communications Inc. | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* This software was developed in part by Philip Paeps under contract for | * This software was developed in part by Philip Paeps under contract for | ||||
* Solarflare Communications, Inc. | * Solarflare Communications, Inc. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions are met: | ||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | * | ||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | * 1. Redistributions of source code must retain the above copyright notice, | ||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | * this list of conditions and the following disclaimer. | ||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | * this list of conditions and the following disclaimer in the documentation | ||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | * and/or other materials provided with the distribution. | ||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | * | ||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||
* SUCH DAMAGE. | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
* | |||||
* The views and conclusions contained in the software and documentation are | |||||
* those of the authors and should not be interpreted as representing official | |||||
* policies, either expressed or implied, of the FreeBSD Project. | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/taskqueue.h> | #include <sys/taskqueue.h> | ||||
#include <sys/malloc.h> | |||||
#include "common/efx.h" | #include "common/efx.h" | ||||
#include "common/efx_mcdi.h" | #include "common/efx_mcdi.h" | ||||
#include "common/efx_regs_mcdi.h" | #include "common/efx_regs_mcdi.h" | ||||
#include "sfxge.h" | #include "sfxge.h" | ||||
#define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ | #define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ | ||||
#define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ | #define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ | ||||
#define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ | #define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ | ||||
/* Acquire exclusive access to MCDI for the duration of a request. */ | |||||
static void | static void | ||||
sfxge_mcdi_acquire(struct sfxge_mcdi *mcdi) | |||||
{ | |||||
SFXGE_MCDI_LOCK(mcdi); | |||||
KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED, | |||||
("MCDI not initialized")); | |||||
while (mcdi->state != SFXGE_MCDI_INITIALIZED) | |||||
(void)cv_wait_sig(&mcdi->cv, &mcdi->lock); | |||||
mcdi->state = SFXGE_MCDI_BUSY; | |||||
SFXGE_MCDI_UNLOCK(mcdi); | |||||
} | |||||
/* Release ownership of MCDI on request completion. */ | |||||
static void | |||||
sfxge_mcdi_release(struct sfxge_mcdi *mcdi) | |||||
{ | |||||
SFXGE_MCDI_LOCK(mcdi); | |||||
KASSERT((mcdi->state == SFXGE_MCDI_BUSY || | |||||
mcdi->state == SFXGE_MCDI_COMPLETED), | |||||
("MCDI not busy or task not completed")); | |||||
mcdi->state = SFXGE_MCDI_INITIALIZED; | |||||
cv_broadcast(&mcdi->cv); | |||||
SFXGE_MCDI_UNLOCK(mcdi); | |||||
} | |||||
static void | |||||
sfxge_mcdi_timeout(struct sfxge_softc *sc) | sfxge_mcdi_timeout(struct sfxge_softc *sc) | ||||
{ | { | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), | log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), | ||||
device_get_unit(dev)); | device_get_unit(dev)); | ||||
EFSYS_PROBE(mcdi_timeout); | EFSYS_PROBE(mcdi_timeout); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) | sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) | ||||
{ | { | ||||
struct sfxge_softc *sc; | struct sfxge_softc *sc; | ||||
struct sfxge_mcdi *mcdi; | struct sfxge_mcdi *mcdi; | ||||
sc = (struct sfxge_softc *)arg; | sc = (struct sfxge_softc *)arg; | ||||
mcdi = &sc->mcdi; | mcdi = &sc->mcdi; | ||||
sfxge_mcdi_acquire(mcdi); | SFXGE_MCDI_LOCK(mcdi); | ||||
KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, | |||||
("MCDI not initialized")); | |||||
/* Issue request and poll for completion. */ | /* Issue request and poll for completion. */ | ||||
efx_mcdi_request_start(sc->enp, emrp, B_FALSE); | efx_mcdi_request_start(sc->enp, emrp, B_FALSE); | ||||
sfxge_mcdi_poll(sc); | sfxge_mcdi_poll(sc); | ||||
sfxge_mcdi_release(mcdi); | SFXGE_MCDI_UNLOCK(mcdi); | ||||
} | } | ||||
static void | static void | ||||
sfxge_mcdi_ev_cpl(void *arg) | sfxge_mcdi_ev_cpl(void *arg) | ||||
{ | { | ||||
struct sfxge_softc *sc; | struct sfxge_softc *sc; | ||||
struct sfxge_mcdi *mcdi; | struct sfxge_mcdi *mcdi; | ||||
sc = (struct sfxge_softc *)arg; | sc = (struct sfxge_softc *)arg; | ||||
mcdi = &sc->mcdi; | mcdi = &sc->mcdi; | ||||
SFXGE_MCDI_LOCK(mcdi); | KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, | ||||
KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy")); | ("MCDI not initialized")); | ||||
mcdi->state = SFXGE_MCDI_COMPLETED; | |||||
cv_broadcast(&mcdi->cv); | /* We do not use MCDI completion, MCDI is simply polled */ | ||||
SFXGE_MCDI_UNLOCK(mcdi); | |||||
} | } | ||||
static void | static void | ||||
sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) | sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) | ||||
{ | { | ||||
struct sfxge_softc *sc; | struct sfxge_softc *sc; | ||||
device_t dev; | device_t dev; | ||||
sc = (struct sfxge_softc *)arg; | sc = (struct sfxge_softc *)arg; | ||||
dev = sc->dev; | dev = sc->dev; | ||||
log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), | log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), | ||||
device_get_unit(dev), | device_get_unit(dev), | ||||
(eme == EFX_MCDI_EXCEPTION_MC_REBOOT) | (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) | ||||
? "REBOOT" | ? "REBOOT" | ||||
: (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) | : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) | ||||
? "BADASSERT" : "UNKNOWN"); | ? "BADASSERT" : "UNKNOWN"); | ||||
EFSYS_PROBE(mcdi_exception); | EFSYS_PROBE(mcdi_exception); | ||||
sfxge_schedule_reset(sc); | sfxge_schedule_reset(sc); | ||||
} | } | ||||
int | int | ||||
sfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) | |||||
{ | |||||
const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); | |||||
struct sfxge_mcdi *mp = &(sc->mcdi); | |||||
efx_mcdi_req_t emr; | |||||
uint8_t *mcdibuf; | |||||
int rc; | |||||
if (mp->state == SFXGE_MCDI_UNINITIALIZED) { | |||||
rc = ENODEV; | |||||
goto fail1; | |||||
} | |||||
if (!(encp->enc_features & EFX_FEATURE_MCDI)) { | |||||
rc = ENOTSUP; | |||||
goto fail2; | |||||
} | |||||
if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) { | |||||
rc = EINVAL; | |||||
goto fail3; | |||||
} | |||||
mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO); | |||||
if (mcdibuf == NULL) { | |||||
rc = ENOMEM; | |||||
goto fail4; | |||||
} | |||||
if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) { | |||||
goto fail5; | |||||
} | |||||
emr.emr_cmd = ip->u.mcdi.cmd; | |||||
emr.emr_in_buf = mcdibuf; | |||||
emr.emr_in_length = ip->u.mcdi.len; | |||||
emr.emr_out_buf = mcdibuf; | |||||
emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD; | |||||
sfxge_mcdi_execute(sc, &emr); | |||||
ip->u.mcdi.rc = emr.emr_rc; | |||||
ip->u.mcdi.cmd = emr.emr_cmd; | |||||
ip->u.mcdi.len = emr.emr_out_length_used; | |||||
if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) { | |||||
goto fail6; | |||||
} | |||||
/* | |||||
* Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT | |||||
* Both ports will see ->emt_exception callbacks on the next MCDI poll | |||||
*/ | |||||
if (ip->u.mcdi.cmd == MC_CMD_REBOOT) { | |||||
EFSYS_PROBE(mcdi_ioctl_mc_reboot); | |||||
/* sfxge_t->s_state_lock held */ | |||||
(void) sfxge_schedule_reset(sc); | |||||
} | |||||
free(mcdibuf, M_TEMP); | |||||
return (0); | |||||
fail6: | |||||
fail5: | |||||
free(mcdibuf, M_TEMP); | |||||
fail4: | |||||
fail3: | |||||
fail2: | |||||
fail1: | |||||
return (rc); | |||||
} | |||||
int | |||||
sfxge_mcdi_init(struct sfxge_softc *sc) | sfxge_mcdi_init(struct sfxge_softc *sc) | ||||
{ | { | ||||
efx_nic_t *enp; | efx_nic_t *enp; | ||||
struct sfxge_mcdi *mcdi; | struct sfxge_mcdi *mcdi; | ||||
efx_mcdi_transport_t *emtp; | efx_mcdi_transport_t *emtp; | ||||
efsys_mem_t *esmp; | |||||
int max_msg_size; | |||||
int rc; | int rc; | ||||
enp = sc->enp; | enp = sc->enp; | ||||
mcdi = &sc->mcdi; | mcdi = &sc->mcdi; | ||||
emtp = &mcdi->transport; | emtp = &mcdi->transport; | ||||
esmp = &mcdi->mem; | |||||
max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; | |||||
KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, | KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, | ||||
("MCDI already initialized")); | ("MCDI already initialized")); | ||||
SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev)); | SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev)); | ||||
mcdi->state = SFXGE_MCDI_INITIALIZED; | mcdi->state = SFXGE_MCDI_INITIALIZED; | ||||
if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0) | |||||
goto fail; | |||||
emtp->emt_context = sc; | emtp->emt_context = sc; | ||||
emtp->emt_dma_mem = esmp; | |||||
emtp->emt_execute = sfxge_mcdi_execute; | emtp->emt_execute = sfxge_mcdi_execute; | ||||
emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; | emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; | ||||
emtp->emt_exception = sfxge_mcdi_exception; | emtp->emt_exception = sfxge_mcdi_exception; | ||||
cv_init(&mcdi->cv, "sfxge_mcdi"); | |||||
if ((rc = efx_mcdi_init(enp, emtp)) != 0) | if ((rc = efx_mcdi_init(enp, emtp)) != 0) | ||||
goto fail; | goto fail; | ||||
return (0); | return (0); | ||||
fail: | fail: | ||||
SFXGE_MCDI_LOCK_DESTROY(mcdi); | SFXGE_MCDI_LOCK_DESTROY(mcdi); | ||||
mcdi->state = SFXGE_MCDI_UNINITIALIZED; | mcdi->state = SFXGE_MCDI_UNINITIALIZED; | ||||
return (rc); | return (rc); | ||||
} | } | ||||
void | void | ||||
sfxge_mcdi_fini(struct sfxge_softc *sc) | sfxge_mcdi_fini(struct sfxge_softc *sc) | ||||
{ | { | ||||
struct sfxge_mcdi *mcdi; | struct sfxge_mcdi *mcdi; | ||||
efx_nic_t *enp; | efx_nic_t *enp; | ||||
efx_mcdi_transport_t *emtp; | efx_mcdi_transport_t *emtp; | ||||
efsys_mem_t *esmp; | |||||
enp = sc->enp; | enp = sc->enp; | ||||
mcdi = &sc->mcdi; | mcdi = &sc->mcdi; | ||||
emtp = &mcdi->transport; | emtp = &mcdi->transport; | ||||
esmp = &mcdi->mem; | |||||
SFXGE_MCDI_LOCK(mcdi); | SFXGE_MCDI_LOCK(mcdi); | ||||
KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, | KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, | ||||
("MCDI not initialized")); | ("MCDI not initialized")); | ||||
efx_mcdi_fini(enp); | efx_mcdi_fini(enp); | ||||
bzero(emtp, sizeof(*emtp)); | bzero(emtp, sizeof(*emtp)); | ||||
cv_destroy(&mcdi->cv); | |||||
SFXGE_MCDI_UNLOCK(mcdi); | SFXGE_MCDI_UNLOCK(mcdi); | ||||
sfxge_dma_free(esmp); | |||||
SFXGE_MCDI_LOCK_DESTROY(mcdi); | SFXGE_MCDI_LOCK_DESTROY(mcdi); | ||||
} | } |