xref: /freebsd/sys/dev/sfxge/sfxge_mcdi.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
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 	SFXGE_MCDI_LOCK(mcdi);
56 	KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED,
57 	    ("MCDI not initialized"));
58 
59 	while (mcdi->state != SFXGE_MCDI_INITIALIZED)
60 		(void)cv_wait_sig(&mcdi->cv, &mcdi->lock);
61 	mcdi->state = SFXGE_MCDI_BUSY;
62 
63 	SFXGE_MCDI_UNLOCK(mcdi);
64 }
65 
66 /* Release ownership of MCDI on request completion. */
67 static void
68 sfxge_mcdi_release(struct sfxge_mcdi *mcdi)
69 {
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"));
74 
75 	mcdi->state = SFXGE_MCDI_INITIALIZED;
76 	cv_broadcast(&mcdi->cv);
77 
78 	SFXGE_MCDI_UNLOCK(mcdi);
79 }
80 
81 static void
82 sfxge_mcdi_timeout(struct sfxge_softc *sc)
83 {
84 	device_t dev = sc->dev;
85 
86 	log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
87 		device_get_unit(dev));
88 
89 	EFSYS_PROBE(mcdi_timeout);
90 	sfxge_schedule_reset(sc);
91 }
92 
93 static void
94 sfxge_mcdi_poll(struct sfxge_softc *sc)
95 {
96 	efx_nic_t *enp;
97 	clock_t delay_total;
98 	clock_t delay_us;
99 	boolean_t aborted;
100 
101 	delay_total = 0;
102 	delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
103 	enp = sc->enp;
104 
105 	do {
106 		if (efx_mcdi_request_poll(enp)) {
107 			EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
108 			return;
109 		}
110 
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);
115 			return;
116 		}
117 
118 		/* Spin or block depending on delay interval. */
119 		if (delay_us < 1000000)
120 			DELAY(delay_us);
121 		else
122 			pause("mcdi wait", delay_us * hz / 1000000);
123 
124 		delay_total += delay_us;
125 
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;
130 
131 	} while (1);
132 }
133 
134 static void
135 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
136 {
137 	struct sfxge_softc *sc;
138 	struct sfxge_mcdi *mcdi;
139 
140 	sc = (struct sfxge_softc *)arg;
141 	mcdi = &sc->mcdi;
142 
143 	sfxge_mcdi_acquire(mcdi);
144 
145 	/* Issue request and poll for completion. */
146 	efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
147 	sfxge_mcdi_poll(sc);
148 
149 	sfxge_mcdi_release(mcdi);
150 }
151 
152 static void
153 sfxge_mcdi_ev_cpl(void *arg)
154 {
155 	struct sfxge_softc *sc;
156 	struct sfxge_mcdi *mcdi;
157 
158 	sc = (struct sfxge_softc *)arg;
159 	mcdi = &sc->mcdi;
160 
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);
166 }
167 
168 static void
169 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
170 {
171 	struct sfxge_softc *sc;
172 	device_t dev;
173 
174 	sc = (struct sfxge_softc *)arg;
175 	dev = sc->dev;
176 
177 	log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
178 	    device_get_unit(dev),
179 	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
180 	    ? "REBOOT"
181 	    : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
182 	    ? "BADASSERT" : "UNKNOWN");
183 
184 	EFSYS_PROBE(mcdi_exception);
185 
186 	sfxge_schedule_reset(sc);
187 }
188 
189 int
190 sfxge_mcdi_init(struct sfxge_softc *sc)
191 {
192 	efx_nic_t *enp;
193 	struct sfxge_mcdi *mcdi;
194 	efx_mcdi_transport_t *emtp;
195 	int rc;
196 
197 	enp = sc->enp;
198 	mcdi = &sc->mcdi;
199 	emtp = &mcdi->transport;
200 
201 	KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
202 	    ("MCDI already initialized"));
203 
204 	SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev));
205 
206 	mcdi->state = SFXGE_MCDI_INITIALIZED;
207 
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;
212 
213 	cv_init(&mcdi->cv, "sfxge_mcdi");
214 
215 	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
216 		goto fail;
217 
218 	return (0);
219 
220 fail:
221 	SFXGE_MCDI_LOCK_DESTROY(mcdi);
222 	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
223 	return (rc);
224 }
225 
226 void
227 sfxge_mcdi_fini(struct sfxge_softc *sc)
228 {
229 	struct sfxge_mcdi *mcdi;
230 	efx_nic_t *enp;
231 	efx_mcdi_transport_t *emtp;
232 
233 	enp = sc->enp;
234 	mcdi = &sc->mcdi;
235 	emtp = &mcdi->transport;
236 
237 	SFXGE_MCDI_LOCK(mcdi);
238 	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
239 	    ("MCDI not initialized"));
240 
241 	efx_mcdi_fini(enp);
242 	bzero(emtp, sizeof(*emtp));
243 
244 	cv_destroy(&mcdi->cv);
245 	SFXGE_MCDI_UNLOCK(mcdi);
246 
247 	SFXGE_MCDI_LOCK_DESTROY(mcdi);
248 }
249