sfxge_mcdi.c revision 283514
1227569Sphilip/*-
2283514Sarybchik * Copyright (c) 2010-2015 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
9283514Sarybchik * modification, are permitted provided that the following conditions are met:
10227569Sphilip *
11283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
12283514Sarybchik *    this list of conditions and the following disclaimer.
13283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
14283514Sarybchik *    this list of conditions and the following disclaimer in the documentation
15283514Sarybchik *    and/or other materials provided with the distribution.
16283514Sarybchik *
17283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28283514Sarybchik *
29283514Sarybchik * The views and conclusions contained in the software and documentation are
30283514Sarybchik * those of the authors and should not be interpreted as representing official
31283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
32227569Sphilip */
33227569Sphilip
34227569Sphilip#include <sys/cdefs.h>
35227569Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/sfxge_mcdi.c 283514 2015-05-25 08:34:55Z arybchik $");
36227569Sphilip
37227569Sphilip#include <sys/param.h>
38227569Sphilip#include <sys/condvar.h>
39227569Sphilip#include <sys/lock.h>
40227569Sphilip#include <sys/mutex.h>
41227569Sphilip#include <sys/proc.h>
42227569Sphilip#include <sys/syslog.h>
43227569Sphilip#include <sys/taskqueue.h>
44283514Sarybchik#include <sys/malloc.h>
45227569Sphilip
46227569Sphilip#include "common/efx.h"
47227569Sphilip#include "common/efx_mcdi.h"
48227569Sphilip#include "common/efx_regs_mcdi.h"
49227569Sphilip
50227569Sphilip#include "sfxge.h"
51227569Sphilip
52227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MIN 10		/* 10us in 1us units */
53227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MAX 100000	/* 100ms in 1us units */
54227569Sphilip#define	SFXGE_MCDI_WATCHDOG_INTERVAL 10000000	/* 10s in 1us units */
55227569Sphilip
56227569Sphilipstatic void
57227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc)
58227569Sphilip{
59227569Sphilip	device_t dev = sc->dev;
60227569Sphilip
61227569Sphilip	log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
62227569Sphilip		device_get_unit(dev));
63227569Sphilip
64227569Sphilip	EFSYS_PROBE(mcdi_timeout);
65227569Sphilip	sfxge_schedule_reset(sc);
66227569Sphilip}
67227569Sphilip
68227569Sphilipstatic void
69227569Sphilipsfxge_mcdi_poll(struct sfxge_softc *sc)
70227569Sphilip{
71227569Sphilip	efx_nic_t *enp;
72227569Sphilip	clock_t delay_total;
73227569Sphilip	clock_t delay_us;
74227569Sphilip	boolean_t aborted;
75227569Sphilip
76227569Sphilip	delay_total = 0;
77227569Sphilip	delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
78227569Sphilip	enp = sc->enp;
79227569Sphilip
80227569Sphilip	do {
81227569Sphilip		if (efx_mcdi_request_poll(enp)) {
82227569Sphilip			EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
83227569Sphilip			return;
84227569Sphilip		}
85227569Sphilip
86227569Sphilip		if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) {
87227569Sphilip			aborted = efx_mcdi_request_abort(enp);
88227569Sphilip			KASSERT(aborted, ("abort failed"));
89227569Sphilip			sfxge_mcdi_timeout(sc);
90227569Sphilip			return;
91227569Sphilip		}
92227569Sphilip
93227569Sphilip		/* Spin or block depending on delay interval. */
94227569Sphilip		if (delay_us < 1000000)
95227569Sphilip			DELAY(delay_us);
96227569Sphilip		else
97227569Sphilip			pause("mcdi wait", delay_us * hz / 1000000);
98227569Sphilip
99227569Sphilip		delay_total += delay_us;
100227569Sphilip
101227569Sphilip		/* Exponentially back off the poll frequency. */
102227569Sphilip		delay_us = delay_us * 2;
103227569Sphilip		if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
104227569Sphilip			delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
105227569Sphilip
106227569Sphilip	} while (1);
107227569Sphilip}
108227569Sphilip
109227569Sphilipstatic void
110227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
111227569Sphilip{
112227569Sphilip	struct sfxge_softc *sc;
113227569Sphilip	struct sfxge_mcdi *mcdi;
114227569Sphilip
115227569Sphilip	sc = (struct sfxge_softc *)arg;
116227569Sphilip	mcdi = &sc->mcdi;
117227569Sphilip
118283514Sarybchik	SFXGE_MCDI_LOCK(mcdi);
119227569Sphilip
120283514Sarybchik	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
121283514Sarybchik	    ("MCDI not initialized"));
122283514Sarybchik
123227569Sphilip	/* Issue request and poll for completion. */
124227569Sphilip	efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
125227569Sphilip	sfxge_mcdi_poll(sc);
126227569Sphilip
127283514Sarybchik	SFXGE_MCDI_UNLOCK(mcdi);
128227569Sphilip}
129227569Sphilip
130227569Sphilipstatic void
131227569Sphilipsfxge_mcdi_ev_cpl(void *arg)
132227569Sphilip{
133227569Sphilip	struct sfxge_softc *sc;
134227569Sphilip	struct sfxge_mcdi *mcdi;
135227569Sphilip
136227569Sphilip	sc = (struct sfxge_softc *)arg;
137227569Sphilip	mcdi = &sc->mcdi;
138227569Sphilip
139283514Sarybchik	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
140283514Sarybchik	    ("MCDI not initialized"));
141283514Sarybchik
142283514Sarybchik	/* We do not use MCDI completion, MCDI is simply polled */
143227569Sphilip}
144227569Sphilip
145227569Sphilipstatic void
146227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
147227569Sphilip{
148227569Sphilip	struct sfxge_softc *sc;
149227569Sphilip	device_t dev;
150227569Sphilip
151227569Sphilip	sc = (struct sfxge_softc *)arg;
152227569Sphilip	dev = sc->dev;
153227569Sphilip
154227569Sphilip	log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
155227569Sphilip	    device_get_unit(dev),
156227569Sphilip	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
157227569Sphilip	    ? "REBOOT"
158227569Sphilip	    : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
159227569Sphilip	    ? "BADASSERT" : "UNKNOWN");
160227569Sphilip
161227569Sphilip	EFSYS_PROBE(mcdi_exception);
162227569Sphilip
163227569Sphilip	sfxge_schedule_reset(sc);
164227569Sphilip}
165227569Sphilip
166227569Sphilipint
167283514Sarybchiksfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip)
168283514Sarybchik{
169283514Sarybchik	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp);
170283514Sarybchik	struct sfxge_mcdi *mp = &(sc->mcdi);
171283514Sarybchik	efx_mcdi_req_t emr;
172283514Sarybchik	uint8_t *mcdibuf;
173283514Sarybchik	int rc;
174283514Sarybchik
175283514Sarybchik	if (mp->state == SFXGE_MCDI_UNINITIALIZED) {
176283514Sarybchik		rc = ENODEV;
177283514Sarybchik		goto fail1;
178283514Sarybchik	}
179283514Sarybchik
180283514Sarybchik	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
181283514Sarybchik		rc = ENOTSUP;
182283514Sarybchik		goto fail2;
183283514Sarybchik	}
184283514Sarybchik
185283514Sarybchik	if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) {
186283514Sarybchik		rc = EINVAL;
187283514Sarybchik		goto fail3;
188283514Sarybchik	}
189283514Sarybchik
190283514Sarybchik	mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO);
191283514Sarybchik	if (mcdibuf == NULL) {
192283514Sarybchik		rc = ENOMEM;
193283514Sarybchik		goto fail4;
194283514Sarybchik	}
195283514Sarybchik	if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) {
196283514Sarybchik		goto fail5;
197283514Sarybchik	}
198283514Sarybchik
199283514Sarybchik	emr.emr_cmd = ip->u.mcdi.cmd;
200283514Sarybchik	emr.emr_in_buf = mcdibuf;
201283514Sarybchik	emr.emr_in_length = ip->u.mcdi.len;
202283514Sarybchik
203283514Sarybchik	emr.emr_out_buf = mcdibuf;
204283514Sarybchik	emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD;
205283514Sarybchik
206283514Sarybchik	sfxge_mcdi_execute(sc, &emr);
207283514Sarybchik
208283514Sarybchik	ip->u.mcdi.rc = emr.emr_rc;
209283514Sarybchik	ip->u.mcdi.cmd = emr.emr_cmd;
210283514Sarybchik	ip->u.mcdi.len = emr.emr_out_length_used;
211283514Sarybchik	if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) {
212283514Sarybchik		goto fail6;
213283514Sarybchik	}
214283514Sarybchik
215283514Sarybchik	/*
216283514Sarybchik	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
217283514Sarybchik	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
218283514Sarybchik	 */
219283514Sarybchik	if (ip->u.mcdi.cmd == MC_CMD_REBOOT) {
220283514Sarybchik
221283514Sarybchik		EFSYS_PROBE(mcdi_ioctl_mc_reboot);
222283514Sarybchik		/* sfxge_t->s_state_lock held */
223283514Sarybchik		(void) sfxge_schedule_reset(sc);
224283514Sarybchik	}
225283514Sarybchik
226283514Sarybchik	free(mcdibuf, M_TEMP);
227283514Sarybchik
228283514Sarybchik	return (0);
229283514Sarybchik
230283514Sarybchikfail6:
231283514Sarybchikfail5:
232283514Sarybchik	free(mcdibuf, M_TEMP);
233283514Sarybchikfail4:
234283514Sarybchikfail3:
235283514Sarybchikfail2:
236283514Sarybchikfail1:
237283514Sarybchik	return (rc);
238283514Sarybchik}
239283514Sarybchik
240283514Sarybchik
241283514Sarybchikint
242227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc)
243227569Sphilip{
244227569Sphilip	efx_nic_t *enp;
245227569Sphilip	struct sfxge_mcdi *mcdi;
246227569Sphilip	efx_mcdi_transport_t *emtp;
247283514Sarybchik	efsys_mem_t *esmp;
248283514Sarybchik	int max_msg_size;
249227569Sphilip	int rc;
250227569Sphilip
251227569Sphilip	enp = sc->enp;
252227569Sphilip	mcdi = &sc->mcdi;
253227569Sphilip	emtp = &mcdi->transport;
254283514Sarybchik	esmp = &mcdi->mem;
255283514Sarybchik	max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
256227569Sphilip
257227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
258227569Sphilip	    ("MCDI already initialized"));
259227569Sphilip
260278250Sarybchik	SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev));
261227569Sphilip
262227569Sphilip	mcdi->state = SFXGE_MCDI_INITIALIZED;
263227569Sphilip
264283514Sarybchik	if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0)
265283514Sarybchik		goto fail;
266283514Sarybchik
267227569Sphilip	emtp->emt_context = sc;
268283514Sarybchik	emtp->emt_dma_mem = esmp;
269227569Sphilip	emtp->emt_execute = sfxge_mcdi_execute;
270227569Sphilip	emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
271227569Sphilip	emtp->emt_exception = sfxge_mcdi_exception;
272227569Sphilip
273227569Sphilip	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
274227569Sphilip		goto fail;
275227569Sphilip
276227569Sphilip	return (0);
277227569Sphilip
278227569Sphilipfail:
279278221Sarybchik	SFXGE_MCDI_LOCK_DESTROY(mcdi);
280227569Sphilip	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
281227569Sphilip	return (rc);
282227569Sphilip}
283227569Sphilip
284227569Sphilipvoid
285227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc)
286227569Sphilip{
287227569Sphilip	struct sfxge_mcdi *mcdi;
288227569Sphilip	efx_nic_t *enp;
289227569Sphilip	efx_mcdi_transport_t *emtp;
290283514Sarybchik	efsys_mem_t *esmp;
291227569Sphilip
292227569Sphilip	enp = sc->enp;
293227569Sphilip	mcdi = &sc->mcdi;
294227569Sphilip	emtp = &mcdi->transport;
295283514Sarybchik	esmp = &mcdi->mem;
296227569Sphilip
297278221Sarybchik	SFXGE_MCDI_LOCK(mcdi);
298227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
299227569Sphilip	    ("MCDI not initialized"));
300227569Sphilip
301227569Sphilip	efx_mcdi_fini(enp);
302227569Sphilip	bzero(emtp, sizeof(*emtp));
303227569Sphilip
304278221Sarybchik	SFXGE_MCDI_UNLOCK(mcdi);
305227569Sphilip
306283514Sarybchik	sfxge_dma_free(esmp);
307283514Sarybchik
308278221Sarybchik	SFXGE_MCDI_LOCK_DESTROY(mcdi);
309227569Sphilip}
310