sfxge_mcdi.c revision 278221
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: head/sys/dev/sfxge/sfxge_mcdi.c 278221 2015-02-04 20:03:57Z arybchik $");
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. */
52static void
53sfxge_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. */
67static void
68sfxge_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
81static void
82sfxge_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
93static void
94sfxge_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
134static void
135sfxge_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
152static void
153sfxge_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
168static void
169sfxge_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
189int
190sfxge_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, "sfxge_mcdi");
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
220fail:
221	SFXGE_MCDI_LOCK_DESTROY(mcdi);
222	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
223	return (rc);
224}
225
226void
227sfxge_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