sfxge_mcdi.c revision 227569
1227569Sphilip/*-
2227569Sphilip * Copyright (c) 2010-2011 Solarflare Communications, Inc.
3227569Sphilip * All rights reserved.
4227569Sphilip *
5227569Sphilip * This software was developed in part by Philip Paeps under contract for
6227569Sphilip * Solarflare Communications, Inc.
7227569Sphilip *
8227569Sphilip * Redistribution and use in source and binary forms, with or without
9227569Sphilip * modification, are permitted provided that the following conditions
10227569Sphilip * are met:
11227569Sphilip * 1. Redistributions of source code must retain the above copyright
12227569Sphilip *    notice, this list of conditions and the following disclaimer.
13227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
14227569Sphilip *    notice, this list of conditions and the following disclaimer in the
15227569Sphilip *    documentation and/or other materials provided with the distribution.
16227569Sphilip *
17227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27227569Sphilip * SUCH DAMAGE.
28227569Sphilip */
29227569Sphilip
30227569Sphilip#include <sys/cdefs.h>
31227569Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/sfxge_mcdi.c 227569 2011-11-16 17:11:13Z philip $");
32227569Sphilip
33227569Sphilip#include <sys/param.h>
34227569Sphilip#include <sys/condvar.h>
35227569Sphilip#include <sys/lock.h>
36227569Sphilip#include <sys/mutex.h>
37227569Sphilip#include <sys/proc.h>
38227569Sphilip#include <sys/syslog.h>
39227569Sphilip#include <sys/taskqueue.h>
40227569Sphilip
41227569Sphilip#include "common/efx.h"
42227569Sphilip#include "common/efx_mcdi.h"
43227569Sphilip#include "common/efx_regs_mcdi.h"
44227569Sphilip
45227569Sphilip#include "sfxge.h"
46227569Sphilip
47227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MIN 10		/* 10us in 1us units */
48227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MAX 100000	/* 100ms in 1us units */
49227569Sphilip#define	SFXGE_MCDI_WATCHDOG_INTERVAL 10000000	/* 10s in 1us units */
50227569Sphilip
51227569Sphilip/* Acquire exclusive access to MCDI for the duration of a request. */
52227569Sphilipstatic void
53227569Sphilipsfxge_mcdi_acquire(struct sfxge_mcdi *mcdi)
54227569Sphilip{
55227569Sphilip
56227569Sphilip	mtx_lock(&mcdi->lock);
57227569Sphilip	KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED,
58227569Sphilip	    ("MCDI not initialized"));
59227569Sphilip
60227569Sphilip	while (mcdi->state != SFXGE_MCDI_INITIALIZED)
61227569Sphilip		(void)cv_wait_sig(&mcdi->cv, &mcdi->lock);
62227569Sphilip	mcdi->state = SFXGE_MCDI_BUSY;
63227569Sphilip
64227569Sphilip	mtx_unlock(&mcdi->lock);
65227569Sphilip}
66227569Sphilip
67227569Sphilip/* Release ownership of MCDI on request completion. */
68227569Sphilipstatic void
69227569Sphilipsfxge_mcdi_release(struct sfxge_mcdi *mcdi)
70227569Sphilip{
71227569Sphilip
72227569Sphilip	mtx_lock(&mcdi->lock);
73227569Sphilip	KASSERT((mcdi->state == SFXGE_MCDI_BUSY ||
74227569Sphilip	    mcdi->state == SFXGE_MCDI_COMPLETED),
75227569Sphilip	    ("MCDI not busy or task not completed"));
76227569Sphilip
77227569Sphilip	mcdi->state = SFXGE_MCDI_INITIALIZED;
78227569Sphilip	cv_broadcast(&mcdi->cv);
79227569Sphilip
80227569Sphilip	mtx_unlock(&mcdi->lock);
81227569Sphilip}
82227569Sphilip
83227569Sphilipstatic void
84227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc)
85227569Sphilip{
86227569Sphilip	device_t dev = sc->dev;
87227569Sphilip
88227569Sphilip	log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
89227569Sphilip		device_get_unit(dev));
90227569Sphilip
91227569Sphilip	EFSYS_PROBE(mcdi_timeout);
92227569Sphilip	sfxge_schedule_reset(sc);
93227569Sphilip}
94227569Sphilip
95227569Sphilipstatic void
96227569Sphilipsfxge_mcdi_poll(struct sfxge_softc *sc)
97227569Sphilip{
98227569Sphilip	efx_nic_t *enp;
99227569Sphilip	clock_t delay_total;
100227569Sphilip	clock_t delay_us;
101227569Sphilip	boolean_t aborted;
102227569Sphilip
103227569Sphilip	delay_total = 0;
104227569Sphilip	delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
105227569Sphilip	enp = sc->enp;
106227569Sphilip
107227569Sphilip	do {
108227569Sphilip		if (efx_mcdi_request_poll(enp)) {
109227569Sphilip			EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
110227569Sphilip			return;
111227569Sphilip		}
112227569Sphilip
113227569Sphilip		if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) {
114227569Sphilip			aborted = efx_mcdi_request_abort(enp);
115227569Sphilip			KASSERT(aborted, ("abort failed"));
116227569Sphilip			sfxge_mcdi_timeout(sc);
117227569Sphilip			return;
118227569Sphilip		}
119227569Sphilip
120227569Sphilip		/* Spin or block depending on delay interval. */
121227569Sphilip		if (delay_us < 1000000)
122227569Sphilip			DELAY(delay_us);
123227569Sphilip		else
124227569Sphilip			pause("mcdi wait", delay_us * hz / 1000000);
125227569Sphilip
126227569Sphilip		delay_total += delay_us;
127227569Sphilip
128227569Sphilip		/* Exponentially back off the poll frequency. */
129227569Sphilip		delay_us = delay_us * 2;
130227569Sphilip		if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
131227569Sphilip			delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
132227569Sphilip
133227569Sphilip	} while (1);
134227569Sphilip}
135227569Sphilip
136227569Sphilipstatic void
137227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
138227569Sphilip{
139227569Sphilip	struct sfxge_softc *sc;
140227569Sphilip	struct sfxge_mcdi *mcdi;
141227569Sphilip
142227569Sphilip	sc = (struct sfxge_softc *)arg;
143227569Sphilip	mcdi = &sc->mcdi;
144227569Sphilip
145227569Sphilip	sfxge_mcdi_acquire(mcdi);
146227569Sphilip
147227569Sphilip	/* Issue request and poll for completion. */
148227569Sphilip	efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
149227569Sphilip	sfxge_mcdi_poll(sc);
150227569Sphilip
151227569Sphilip	sfxge_mcdi_release(mcdi);
152227569Sphilip}
153227569Sphilip
154227569Sphilipstatic void
155227569Sphilipsfxge_mcdi_ev_cpl(void *arg)
156227569Sphilip{
157227569Sphilip	struct sfxge_softc *sc;
158227569Sphilip	struct sfxge_mcdi *mcdi;
159227569Sphilip
160227569Sphilip	sc = (struct sfxge_softc *)arg;
161227569Sphilip	mcdi = &sc->mcdi;
162227569Sphilip
163227569Sphilip	mtx_lock(&mcdi->lock);
164227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy"));
165227569Sphilip	mcdi->state = SFXGE_MCDI_COMPLETED;
166227569Sphilip	cv_broadcast(&mcdi->cv);
167227569Sphilip	mtx_unlock(&mcdi->lock);
168227569Sphilip}
169227569Sphilip
170227569Sphilipstatic void
171227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
172227569Sphilip{
173227569Sphilip	struct sfxge_softc *sc;
174227569Sphilip	device_t dev;
175227569Sphilip
176227569Sphilip	sc = (struct sfxge_softc *)arg;
177227569Sphilip	dev = sc->dev;
178227569Sphilip
179227569Sphilip	log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
180227569Sphilip	    device_get_unit(dev),
181227569Sphilip	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
182227569Sphilip	    ? "REBOOT"
183227569Sphilip	    : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
184227569Sphilip	    ? "BADASSERT" : "UNKNOWN");
185227569Sphilip
186227569Sphilip	EFSYS_PROBE(mcdi_exception);
187227569Sphilip
188227569Sphilip	sfxge_schedule_reset(sc);
189227569Sphilip}
190227569Sphilip
191227569Sphilipint
192227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc)
193227569Sphilip{
194227569Sphilip	efx_nic_t *enp;
195227569Sphilip	struct sfxge_mcdi *mcdi;
196227569Sphilip	efx_mcdi_transport_t *emtp;
197227569Sphilip	int rc;
198227569Sphilip
199227569Sphilip	enp = sc->enp;
200227569Sphilip	mcdi = &sc->mcdi;
201227569Sphilip	emtp = &mcdi->transport;
202227569Sphilip
203227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
204227569Sphilip	    ("MCDI already initialized"));
205227569Sphilip
206227569Sphilip	mtx_init(&mcdi->lock, "sfxge_mcdi", NULL, MTX_DEF);
207227569Sphilip
208227569Sphilip	mcdi->state = SFXGE_MCDI_INITIALIZED;
209227569Sphilip
210227569Sphilip	emtp->emt_context = sc;
211227569Sphilip	emtp->emt_execute = sfxge_mcdi_execute;
212227569Sphilip	emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
213227569Sphilip	emtp->emt_exception = sfxge_mcdi_exception;
214227569Sphilip
215227569Sphilip	cv_init(&mcdi->cv, "sfxge_mcdi");
216227569Sphilip
217227569Sphilip	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
218227569Sphilip		goto fail;
219227569Sphilip
220227569Sphilip	return (0);
221227569Sphilip
222227569Sphilipfail:
223227569Sphilip	mtx_destroy(&mcdi->lock);
224227569Sphilip	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
225227569Sphilip	return (rc);
226227569Sphilip}
227227569Sphilip
228227569Sphilipvoid
229227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc)
230227569Sphilip{
231227569Sphilip	struct sfxge_mcdi *mcdi;
232227569Sphilip	efx_nic_t *enp;
233227569Sphilip	efx_mcdi_transport_t *emtp;
234227569Sphilip
235227569Sphilip	enp = sc->enp;
236227569Sphilip	mcdi = &sc->mcdi;
237227569Sphilip	emtp = &mcdi->transport;
238227569Sphilip
239227569Sphilip	mtx_lock(&mcdi->lock);
240227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
241227569Sphilip	    ("MCDI not initialized"));
242227569Sphilip
243227569Sphilip	efx_mcdi_fini(enp);
244227569Sphilip	bzero(emtp, sizeof(*emtp));
245227569Sphilip
246227569Sphilip	cv_destroy(&mcdi->cv);
247227569Sphilip	mtx_unlock(&mcdi->lock);
248227569Sphilip
249227569Sphilip	mtx_destroy(&mcdi->lock);
250227569Sphilip}
251