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. */
52static void
53sfxge_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. */
68static void
69sfxge_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
83static void
84sfxge_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
95static void
96sfxge_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
136static void
137sfxge_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
154static void
155sfxge_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
170static void
171sfxge_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
191int
192sfxge_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
222fail:
223	mtx_destroy(&mcdi->lock);
224	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
225	return (rc);
226}
227
228void
229sfxge_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