2 * Copyright (c) 2010-2011 Solarflare Communications, Inc.
5 * This software was developed in part by Philip Paeps under contract for
6 * Solarflare Communications, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/condvar.h>
36 #include <sys/mutex.h>
38 #include <sys/syslog.h>
39 #include <sys/taskqueue.h>
41 #include "common/efx.h"
42 #include "common/efx_mcdi.h"
43 #include "common/efx_regs_mcdi.h"
47 #define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */
48 #define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */
49 #define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */
51 /* Acquire exclusive access to MCDI for the duration of a request. */
53 sfxge_mcdi_acquire(struct sfxge_mcdi *mcdi)
55 SFXGE_MCDI_LOCK(mcdi);
56 KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED,
57 ("MCDI not initialized"));
59 while (mcdi->state != SFXGE_MCDI_INITIALIZED)
60 (void)cv_wait_sig(&mcdi->cv, &mcdi->lock);
61 mcdi->state = SFXGE_MCDI_BUSY;
63 SFXGE_MCDI_UNLOCK(mcdi);
66 /* Release ownership of MCDI on request completion. */
68 sfxge_mcdi_release(struct sfxge_mcdi *mcdi)
70 SFXGE_MCDI_LOCK(mcdi);
71 KASSERT((mcdi->state == SFXGE_MCDI_BUSY ||
72 mcdi->state == SFXGE_MCDI_COMPLETED),
73 ("MCDI not busy or task not completed"));
75 mcdi->state = SFXGE_MCDI_INITIALIZED;
76 cv_broadcast(&mcdi->cv);
78 SFXGE_MCDI_UNLOCK(mcdi);
82 sfxge_mcdi_timeout(struct sfxge_softc *sc)
84 device_t dev = sc->dev;
86 log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
87 device_get_unit(dev));
89 EFSYS_PROBE(mcdi_timeout);
90 sfxge_schedule_reset(sc);
94 sfxge_mcdi_poll(struct sfxge_softc *sc)
102 delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
106 if (efx_mcdi_request_poll(enp)) {
107 EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
111 if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) {
112 aborted = efx_mcdi_request_abort(enp);
113 KASSERT(aborted, ("abort failed"));
114 sfxge_mcdi_timeout(sc);
118 /* Spin or block depending on delay interval. */
119 if (delay_us < 1000000)
122 pause("mcdi wait", delay_us * hz / 1000000);
124 delay_total += delay_us;
126 /* Exponentially back off the poll frequency. */
127 delay_us = delay_us * 2;
128 if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
129 delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
135 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
137 struct sfxge_softc *sc;
138 struct sfxge_mcdi *mcdi;
140 sc = (struct sfxge_softc *)arg;
143 sfxge_mcdi_acquire(mcdi);
145 /* Issue request and poll for completion. */
146 efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
149 sfxge_mcdi_release(mcdi);
153 sfxge_mcdi_ev_cpl(void *arg)
155 struct sfxge_softc *sc;
156 struct sfxge_mcdi *mcdi;
158 sc = (struct sfxge_softc *)arg;
161 SFXGE_MCDI_LOCK(mcdi);
162 KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy"));
163 mcdi->state = SFXGE_MCDI_COMPLETED;
164 cv_broadcast(&mcdi->cv);
165 SFXGE_MCDI_UNLOCK(mcdi);
169 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
171 struct sfxge_softc *sc;
174 sc = (struct sfxge_softc *)arg;
177 log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
178 device_get_unit(dev),
179 (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
181 : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
182 ? "BADASSERT" : "UNKNOWN");
184 EFSYS_PROBE(mcdi_exception);
186 sfxge_schedule_reset(sc);
190 sfxge_mcdi_init(struct sfxge_softc *sc)
193 struct sfxge_mcdi *mcdi;
194 efx_mcdi_transport_t *emtp;
199 emtp = &mcdi->transport;
201 KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
202 ("MCDI already initialized"));
204 SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev));
206 mcdi->state = SFXGE_MCDI_INITIALIZED;
208 emtp->emt_context = sc;
209 emtp->emt_execute = sfxge_mcdi_execute;
210 emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
211 emtp->emt_exception = sfxge_mcdi_exception;
213 cv_init(&mcdi->cv, "sfxge_mcdi");
215 if ((rc = efx_mcdi_init(enp, emtp)) != 0)
221 SFXGE_MCDI_LOCK_DESTROY(mcdi);
222 mcdi->state = SFXGE_MCDI_UNINITIALIZED;
227 sfxge_mcdi_fini(struct sfxge_softc *sc)
229 struct sfxge_mcdi *mcdi;
231 efx_mcdi_transport_t *emtp;
235 emtp = &mcdi->transport;
237 SFXGE_MCDI_LOCK(mcdi);
238 KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
239 ("MCDI not initialized"));
242 bzero(emtp, sizeof(*emtp));
244 cv_destroy(&mcdi->cv);
245 SFXGE_MCDI_UNLOCK(mcdi);
247 SFXGE_MCDI_LOCK_DESTROY(mcdi);