xref: /freebsd/sys/dev/sfxge/sfxge_mcdi.c (revision 3b3a8eb937bf8045231e8364bfd1b94cd4a95979)
1 /*-
2  * Copyright (c) 2010-2011 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
10  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/condvar.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/proc.h>
38 #include <sys/syslog.h>
39 #include <sys/taskqueue.h>
40 
41 #include "common/efx.h"
42 #include "common/efx_mcdi.h"
43 #include "common/efx_regs_mcdi.h"
44 
45 #include "sfxge.h"
46 
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 */
50 
51 /* Acquire exclusive access to MCDI for the duration of a request. */
52 static void
53 sfxge_mcdi_acquire(struct sfxge_mcdi *mcdi)
54 {
55 
56 	mtx_lock(&mcdi->lock);
57 	KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED,
58 	    ("MCDI not initialized"));
59 
60 	while (mcdi->state != SFXGE_MCDI_INITIALIZED)
61 		(void)cv_wait_sig(&mcdi->cv, &mcdi->lock);
62 	mcdi->state = SFXGE_MCDI_BUSY;
63 
64 	mtx_unlock(&mcdi->lock);
65 }
66 
67 /* Release ownership of MCDI on request completion. */
68 static void
69 sfxge_mcdi_release(struct sfxge_mcdi *mcdi)
70 {
71 
72 	mtx_lock(&mcdi->lock);
73 	KASSERT((mcdi->state == SFXGE_MCDI_BUSY ||
74 	    mcdi->state == SFXGE_MCDI_COMPLETED),
75 	    ("MCDI not busy or task not completed"));
76 
77 	mcdi->state = SFXGE_MCDI_INITIALIZED;
78 	cv_broadcast(&mcdi->cv);
79 
80 	mtx_unlock(&mcdi->lock);
81 }
82 
83 static void
84 sfxge_mcdi_timeout(struct sfxge_softc *sc)
85 {
86 	device_t dev = sc->dev;
87 
88 	log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
89 		device_get_unit(dev));
90 
91 	EFSYS_PROBE(mcdi_timeout);
92 	sfxge_schedule_reset(sc);
93 }
94 
95 static void
96 sfxge_mcdi_poll(struct sfxge_softc *sc)
97 {
98 	efx_nic_t *enp;
99 	clock_t delay_total;
100 	clock_t delay_us;
101 	boolean_t aborted;
102 
103 	delay_total = 0;
104 	delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
105 	enp = sc->enp;
106 
107 	do {
108 		if (efx_mcdi_request_poll(enp)) {
109 			EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
110 			return;
111 		}
112 
113 		if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) {
114 			aborted = efx_mcdi_request_abort(enp);
115 			KASSERT(aborted, ("abort failed"));
116 			sfxge_mcdi_timeout(sc);
117 			return;
118 		}
119 
120 		/* Spin or block depending on delay interval. */
121 		if (delay_us < 1000000)
122 			DELAY(delay_us);
123 		else
124 			pause("mcdi wait", delay_us * hz / 1000000);
125 
126 		delay_total += delay_us;
127 
128 		/* Exponentially back off the poll frequency. */
129 		delay_us = delay_us * 2;
130 		if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
131 			delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
132 
133 	} while (1);
134 }
135 
136 static void
137 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
138 {
139 	struct sfxge_softc *sc;
140 	struct sfxge_mcdi *mcdi;
141 
142 	sc = (struct sfxge_softc *)arg;
143 	mcdi = &sc->mcdi;
144 
145 	sfxge_mcdi_acquire(mcdi);
146 
147 	/* Issue request and poll for completion. */
148 	efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
149 	sfxge_mcdi_poll(sc);
150 
151 	sfxge_mcdi_release(mcdi);
152 }
153 
154 static void
155 sfxge_mcdi_ev_cpl(void *arg)
156 {
157 	struct sfxge_softc *sc;
158 	struct sfxge_mcdi *mcdi;
159 
160 	sc = (struct sfxge_softc *)arg;
161 	mcdi = &sc->mcdi;
162 
163 	mtx_lock(&mcdi->lock);
164 	KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy"));
165 	mcdi->state = SFXGE_MCDI_COMPLETED;
166 	cv_broadcast(&mcdi->cv);
167 	mtx_unlock(&mcdi->lock);
168 }
169 
170 static void
171 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
172 {
173 	struct sfxge_softc *sc;
174 	device_t dev;
175 
176 	sc = (struct sfxge_softc *)arg;
177 	dev = sc->dev;
178 
179 	log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
180 	    device_get_unit(dev),
181 	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
182 	    ? "REBOOT"
183 	    : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
184 	    ? "BADASSERT" : "UNKNOWN");
185 
186 	EFSYS_PROBE(mcdi_exception);
187 
188 	sfxge_schedule_reset(sc);
189 }
190 
191 int
192 sfxge_mcdi_init(struct sfxge_softc *sc)
193 {
194 	efx_nic_t *enp;
195 	struct sfxge_mcdi *mcdi;
196 	efx_mcdi_transport_t *emtp;
197 	int rc;
198 
199 	enp = sc->enp;
200 	mcdi = &sc->mcdi;
201 	emtp = &mcdi->transport;
202 
203 	KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
204 	    ("MCDI already initialized"));
205 
206 	mtx_init(&mcdi->lock, "sfxge_mcdi", NULL, MTX_DEF);
207 
208 	mcdi->state = SFXGE_MCDI_INITIALIZED;
209 
210 	emtp->emt_context = sc;
211 	emtp->emt_execute = sfxge_mcdi_execute;
212 	emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
213 	emtp->emt_exception = sfxge_mcdi_exception;
214 
215 	cv_init(&mcdi->cv, "sfxge_mcdi");
216 
217 	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
218 		goto fail;
219 
220 	return (0);
221 
222 fail:
223 	mtx_destroy(&mcdi->lock);
224 	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
225 	return (rc);
226 }
227 
228 void
229 sfxge_mcdi_fini(struct sfxge_softc *sc)
230 {
231 	struct sfxge_mcdi *mcdi;
232 	efx_nic_t *enp;
233 	efx_mcdi_transport_t *emtp;
234 
235 	enp = sc->enp;
236 	mcdi = &sc->mcdi;
237 	emtp = &mcdi->transport;
238 
239 	mtx_lock(&mcdi->lock);
240 	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
241 	    ("MCDI not initialized"));
242 
243 	efx_mcdi_fini(enp);
244 	bzero(emtp, sizeof(*emtp));
245 
246 	cv_destroy(&mcdi->cv);
247 	mtx_unlock(&mcdi->lock);
248 
249 	mtx_destroy(&mcdi->lock);
250 }
251