1 /*- 2 * Copyright (c) 2010-2015 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * This software was developed in part by Philip Paeps under contract for 6 * Solarflare Communications, Inc. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * The views and conclusions contained in the software and documentation are 30 * those of the authors and should not be interpreted as representing official 31 * policies, either expressed or implied, of the FreeBSD Project. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/condvar.h> 39 #include <sys/lock.h> 40 #include <sys/mutex.h> 41 #include <sys/proc.h> 42 #include <sys/syslog.h> 43 #include <sys/taskqueue.h> 44 #include <sys/malloc.h> 45 46 #include "common/efx.h" 47 #include "common/efx_mcdi.h" 48 #include "common/efx_regs_mcdi.h" 49 50 #include "sfxge.h" 51 52 #define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */ 53 #define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */ 54 #define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ 55 56 static void 57 sfxge_mcdi_timeout(struct sfxge_softc *sc) 58 { 59 device_t dev = sc->dev; 60 61 log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev), 62 device_get_unit(dev)); 63 64 EFSYS_PROBE(mcdi_timeout); 65 sfxge_schedule_reset(sc); 66 } 67 68 static void 69 sfxge_mcdi_poll(struct sfxge_softc *sc) 70 { 71 efx_nic_t *enp; 72 clock_t delay_total; 73 clock_t delay_us; 74 boolean_t aborted; 75 76 delay_total = 0; 77 delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN; 78 enp = sc->enp; 79 80 do { 81 if (efx_mcdi_request_poll(enp)) { 82 EFSYS_PROBE1(mcdi_delay, clock_t, delay_total); 83 return; 84 } 85 86 if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) { 87 aborted = efx_mcdi_request_abort(enp); 88 KASSERT(aborted, ("abort failed")); 89 sfxge_mcdi_timeout(sc); 90 return; 91 } 92 93 /* Spin or block depending on delay interval. */ 94 if (delay_us < 1000000) 95 DELAY(delay_us); 96 else 97 pause("mcdi wait", delay_us * hz / 1000000); 98 99 delay_total += delay_us; 100 101 /* Exponentially back off the poll frequency. */ 102 delay_us = delay_us * 2; 103 if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX) 104 delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX; 105 106 } while (1); 107 } 108 109 static void 110 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 111 { 112 struct sfxge_softc *sc; 113 struct sfxge_mcdi *mcdi; 114 115 sc = (struct sfxge_softc *)arg; 116 mcdi = &sc->mcdi; 117 118 SFXGE_MCDI_LOCK(mcdi); 119 120 KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 121 ("MCDI not initialized")); 122 123 /* Issue request and poll for completion. */ 124 efx_mcdi_request_start(sc->enp, emrp, B_FALSE); 125 sfxge_mcdi_poll(sc); 126 127 SFXGE_MCDI_UNLOCK(mcdi); 128 } 129 130 static void 131 sfxge_mcdi_ev_cpl(void *arg) 132 { 133 struct sfxge_softc *sc; 134 struct sfxge_mcdi *mcdi; 135 136 sc = (struct sfxge_softc *)arg; 137 mcdi = &sc->mcdi; 138 139 KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 140 ("MCDI not initialized")); 141 142 /* We do not use MCDI completion, MCDI is simply polled */ 143 } 144 145 static void 146 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 147 { 148 struct sfxge_softc *sc; 149 device_t dev; 150 151 sc = (struct sfxge_softc *)arg; 152 dev = sc->dev; 153 154 log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev), 155 device_get_unit(dev), 156 (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) 157 ? "REBOOT" 158 : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) 159 ? "BADASSERT" : "UNKNOWN"); 160 161 EFSYS_PROBE(mcdi_exception); 162 163 sfxge_schedule_reset(sc); 164 } 165 166 int 167 sfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip) 168 { 169 const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp); 170 struct sfxge_mcdi *mp = &(sc->mcdi); 171 efx_mcdi_req_t emr; 172 uint8_t *mcdibuf; 173 int rc; 174 175 if (mp->state == SFXGE_MCDI_UNINITIALIZED) { 176 rc = ENODEV; 177 goto fail1; 178 } 179 180 if (!(encp->enc_features & EFX_FEATURE_MCDI)) { 181 rc = ENOTSUP; 182 goto fail2; 183 } 184 185 if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) { 186 rc = EINVAL; 187 goto fail3; 188 } 189 190 mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO); 191 if (mcdibuf == NULL) { 192 rc = ENOMEM; 193 goto fail4; 194 } 195 if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) { 196 goto fail5; 197 } 198 199 emr.emr_cmd = ip->u.mcdi.cmd; 200 emr.emr_in_buf = mcdibuf; 201 emr.emr_in_length = ip->u.mcdi.len; 202 203 emr.emr_out_buf = mcdibuf; 204 emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD; 205 206 sfxge_mcdi_execute(sc, &emr); 207 208 ip->u.mcdi.rc = emr.emr_rc; 209 ip->u.mcdi.cmd = emr.emr_cmd; 210 ip->u.mcdi.len = emr.emr_out_length_used; 211 if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) { 212 goto fail6; 213 } 214 215 /* 216 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT 217 * Both ports will see ->emt_exception callbacks on the next MCDI poll 218 */ 219 if (ip->u.mcdi.cmd == MC_CMD_REBOOT) { 220 221 EFSYS_PROBE(mcdi_ioctl_mc_reboot); 222 /* sfxge_t->s_state_lock held */ 223 (void) sfxge_schedule_reset(sc); 224 } 225 226 free(mcdibuf, M_TEMP); 227 228 return (0); 229 230 fail6: 231 fail5: 232 free(mcdibuf, M_TEMP); 233 fail4: 234 fail3: 235 fail2: 236 fail1: 237 return (rc); 238 } 239 240 241 int 242 sfxge_mcdi_init(struct sfxge_softc *sc) 243 { 244 efx_nic_t *enp; 245 struct sfxge_mcdi *mcdi; 246 efx_mcdi_transport_t *emtp; 247 efsys_mem_t *esmp; 248 int max_msg_size; 249 int rc; 250 251 enp = sc->enp; 252 mcdi = &sc->mcdi; 253 emtp = &mcdi->transport; 254 esmp = &mcdi->mem; 255 max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 256 257 KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED, 258 ("MCDI already initialized")); 259 260 SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev)); 261 262 mcdi->state = SFXGE_MCDI_INITIALIZED; 263 264 if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0) 265 goto fail; 266 267 emtp->emt_context = sc; 268 emtp->emt_dma_mem = esmp; 269 emtp->emt_execute = sfxge_mcdi_execute; 270 emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; 271 emtp->emt_exception = sfxge_mcdi_exception; 272 273 if ((rc = efx_mcdi_init(enp, emtp)) != 0) 274 goto fail; 275 276 return (0); 277 278 fail: 279 SFXGE_MCDI_LOCK_DESTROY(mcdi); 280 mcdi->state = SFXGE_MCDI_UNINITIALIZED; 281 return (rc); 282 } 283 284 void 285 sfxge_mcdi_fini(struct sfxge_softc *sc) 286 { 287 struct sfxge_mcdi *mcdi; 288 efx_nic_t *enp; 289 efx_mcdi_transport_t *emtp; 290 efsys_mem_t *esmp; 291 292 enp = sc->enp; 293 mcdi = &sc->mcdi; 294 emtp = &mcdi->transport; 295 esmp = &mcdi->mem; 296 297 SFXGE_MCDI_LOCK(mcdi); 298 KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED, 299 ("MCDI not initialized")); 300 301 efx_mcdi_fini(enp); 302 bzero(emtp, sizeof(*emtp)); 303 304 SFXGE_MCDI_UNLOCK(mcdi); 305 306 sfxge_dma_free(esmp); 307 308 SFXGE_MCDI_LOCK_DESTROY(mcdi); 309 } 310