1227569Sphilip/*-
2300607Sarybchik * Copyright (c) 2010-2016 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: stable/11/sys/dev/sfxge/sfxge_mcdi.c 311487 2017-01-06 07:20:20Z 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
52291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING
53291843Sarybchik#include <dev/pci/pcivar.h>
54291843Sarybchik#endif
55291843Sarybchik
56227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MIN 10		/* 10us in 1us units */
57227569Sphilip#define	SFXGE_MCDI_POLL_INTERVAL_MAX 100000	/* 100ms in 1us units */
58227569Sphilip
59227569Sphilipstatic void
60227569Sphilipsfxge_mcdi_timeout(struct sfxge_softc *sc)
61227569Sphilip{
62227569Sphilip	device_t dev = sc->dev;
63227569Sphilip
64227569Sphilip	log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
65227569Sphilip		device_get_unit(dev));
66227569Sphilip
67227569Sphilip	EFSYS_PROBE(mcdi_timeout);
68227569Sphilip	sfxge_schedule_reset(sc);
69227569Sphilip}
70227569Sphilip
71227569Sphilipstatic void
72311487Sarybchiksfxge_mcdi_poll(struct sfxge_softc *sc, uint32_t timeout_us)
73227569Sphilip{
74227569Sphilip	efx_nic_t *enp;
75227569Sphilip	clock_t delay_total;
76227569Sphilip	clock_t delay_us;
77227569Sphilip	boolean_t aborted;
78227569Sphilip
79227569Sphilip	delay_total = 0;
80227569Sphilip	delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
81227569Sphilip	enp = sc->enp;
82227569Sphilip
83227569Sphilip	do {
84227569Sphilip		if (efx_mcdi_request_poll(enp)) {
85227569Sphilip			EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
86227569Sphilip			return;
87227569Sphilip		}
88227569Sphilip
89311487Sarybchik		if (delay_total > timeout_us) {
90227569Sphilip			aborted = efx_mcdi_request_abort(enp);
91227569Sphilip			KASSERT(aborted, ("abort failed"));
92227569Sphilip			sfxge_mcdi_timeout(sc);
93227569Sphilip			return;
94227569Sphilip		}
95227569Sphilip
96227569Sphilip		/* Spin or block depending on delay interval. */
97227569Sphilip		if (delay_us < 1000000)
98227569Sphilip			DELAY(delay_us);
99227569Sphilip		else
100227569Sphilip			pause("mcdi wait", delay_us * hz / 1000000);
101227569Sphilip
102227569Sphilip		delay_total += delay_us;
103227569Sphilip
104227569Sphilip		/* Exponentially back off the poll frequency. */
105227569Sphilip		delay_us = delay_us * 2;
106227569Sphilip		if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
107227569Sphilip			delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
108227569Sphilip
109227569Sphilip	} while (1);
110227569Sphilip}
111227569Sphilip
112227569Sphilipstatic void
113227569Sphilipsfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
114227569Sphilip{
115227569Sphilip	struct sfxge_softc *sc;
116227569Sphilip	struct sfxge_mcdi *mcdi;
117311487Sarybchik	uint32_t timeout_us = 0;
118227569Sphilip
119227569Sphilip	sc = (struct sfxge_softc *)arg;
120227569Sphilip	mcdi = &sc->mcdi;
121227569Sphilip
122283514Sarybchik	SFXGE_MCDI_LOCK(mcdi);
123227569Sphilip
124283514Sarybchik	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
125283514Sarybchik	    ("MCDI not initialized"));
126283514Sarybchik
127227569Sphilip	/* Issue request and poll for completion. */
128311487Sarybchik	efx_mcdi_get_timeout(sc->enp, emrp, &timeout_us);
129311487Sarybchik	KASSERT(timeout_us > 0, ("MCDI timeout not initialized"));
130311487Sarybchik
131227569Sphilip	efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
132311487Sarybchik	sfxge_mcdi_poll(sc, timeout_us);
133227569Sphilip
134283514Sarybchik	SFXGE_MCDI_UNLOCK(mcdi);
135227569Sphilip}
136227569Sphilip
137227569Sphilipstatic void
138227569Sphilipsfxge_mcdi_ev_cpl(void *arg)
139227569Sphilip{
140227569Sphilip	struct sfxge_softc *sc;
141227569Sphilip	struct sfxge_mcdi *mcdi;
142227569Sphilip
143227569Sphilip	sc = (struct sfxge_softc *)arg;
144227569Sphilip	mcdi = &sc->mcdi;
145227569Sphilip
146283514Sarybchik	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
147283514Sarybchik	    ("MCDI not initialized"));
148283514Sarybchik
149283514Sarybchik	/* We do not use MCDI completion, MCDI is simply polled */
150227569Sphilip}
151227569Sphilip
152227569Sphilipstatic void
153227569Sphilipsfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
154227569Sphilip{
155227569Sphilip	struct sfxge_softc *sc;
156227569Sphilip	device_t dev;
157227569Sphilip
158227569Sphilip	sc = (struct sfxge_softc *)arg;
159227569Sphilip	dev = sc->dev;
160227569Sphilip
161227569Sphilip	log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
162227569Sphilip	    device_get_unit(dev),
163227569Sphilip	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
164227569Sphilip	    ? "REBOOT"
165227569Sphilip	    : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
166227569Sphilip	    ? "BADASSERT" : "UNKNOWN");
167227569Sphilip
168227569Sphilip	EFSYS_PROBE(mcdi_exception);
169227569Sphilip
170227569Sphilip	sfxge_schedule_reset(sc);
171227569Sphilip}
172227569Sphilip
173291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING
174291843Sarybchik
175291843Sarybchik#define SFXGE_MCDI_LOG_BUF_SIZE 128
176291843Sarybchik
177291843Sarybchikstatic size_t
178291843Sarybchiksfxge_mcdi_do_log(char *buffer, void *data, size_t data_size,
179291843Sarybchik		  size_t pfxsize, size_t position)
180291843Sarybchik{
181291843Sarybchik	uint32_t *words = data;
182291843Sarybchik	size_t i;
183291843Sarybchik
184291843Sarybchik	for (i = 0; i < data_size; i += sizeof(*words)) {
185291843Sarybchik		if (position + 2 * sizeof(*words) + 1 >= SFXGE_MCDI_LOG_BUF_SIZE) {
186291843Sarybchik			buffer[position] = '\0';
187291843Sarybchik			printf("%s \\\n", buffer);
188291843Sarybchik			position = pfxsize;
189291843Sarybchik		}
190291843Sarybchik		snprintf(buffer + position, SFXGE_MCDI_LOG_BUF_SIZE - position,
191291843Sarybchik			 " %08x", *words);
192291843Sarybchik		words++;
193291843Sarybchik		position += 2 * sizeof(uint32_t) + 1;
194291843Sarybchik	}
195291843Sarybchik	return (position);
196291843Sarybchik}
197291843Sarybchik
198291843Sarybchikstatic void
199291843Sarybchiksfxge_mcdi_logger(void *arg, efx_log_msg_t type,
200291843Sarybchik		  void *header, size_t header_size,
201291843Sarybchik		  void *data, size_t data_size)
202291843Sarybchik{
203291843Sarybchik	struct sfxge_softc *sc = (struct sfxge_softc *)arg;
204291843Sarybchik	char buffer[SFXGE_MCDI_LOG_BUF_SIZE];
205291843Sarybchik	size_t pfxsize;
206291843Sarybchik	size_t start;
207291843Sarybchik
208291843Sarybchik	if (!sc->mcdi_logging)
209291843Sarybchik		return;
210291843Sarybchik
211291843Sarybchik	pfxsize = snprintf(buffer, sizeof(buffer),
212291843Sarybchik			   "sfc %04x:%02x:%02x.%02x %s MCDI RPC %s:",
213291843Sarybchik			   pci_get_domain(sc->dev),
214291843Sarybchik			   pci_get_bus(sc->dev),
215291843Sarybchik			   pci_get_slot(sc->dev),
216291843Sarybchik			   pci_get_function(sc->dev),
217291843Sarybchik			   device_get_nameunit(sc->dev),
218291843Sarybchik			   type == EFX_LOG_MCDI_REQUEST ? "REQ" :
219291843Sarybchik			   type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
220291843Sarybchik	start = sfxge_mcdi_do_log(buffer, header, header_size,
221291843Sarybchik				  pfxsize, pfxsize);
222291843Sarybchik	start = sfxge_mcdi_do_log(buffer, data, data_size, pfxsize, start);
223291843Sarybchik	if (start != pfxsize) {
224291843Sarybchik		buffer[start] = '\0';
225291843Sarybchik		printf("%s\n", buffer);
226291843Sarybchik	}
227291843Sarybchik}
228291843Sarybchik
229291843Sarybchik#endif
230291843Sarybchik
231227569Sphilipint
232283514Sarybchiksfxge_mcdi_ioctl(struct sfxge_softc *sc, sfxge_ioc_t *ip)
233283514Sarybchik{
234283514Sarybchik	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sc->enp);
235283514Sarybchik	struct sfxge_mcdi *mp = &(sc->mcdi);
236283514Sarybchik	efx_mcdi_req_t emr;
237283514Sarybchik	uint8_t *mcdibuf;
238283514Sarybchik	int rc;
239283514Sarybchik
240283514Sarybchik	if (mp->state == SFXGE_MCDI_UNINITIALIZED) {
241283514Sarybchik		rc = ENODEV;
242283514Sarybchik		goto fail1;
243283514Sarybchik	}
244283514Sarybchik
245283514Sarybchik	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
246283514Sarybchik		rc = ENOTSUP;
247283514Sarybchik		goto fail2;
248283514Sarybchik	}
249283514Sarybchik
250283514Sarybchik	if (ip->u.mcdi.len > SFXGE_MCDI_MAX_PAYLOAD) {
251283514Sarybchik		rc = EINVAL;
252283514Sarybchik		goto fail3;
253283514Sarybchik	}
254283514Sarybchik
255283514Sarybchik	mcdibuf = malloc(SFXGE_MCDI_MAX_PAYLOAD, M_TEMP, M_WAITOK | M_ZERO);
256283514Sarybchik	if ((rc = copyin(ip->u.mcdi.payload, mcdibuf, ip->u.mcdi.len)) != 0) {
257283514Sarybchik		goto fail5;
258283514Sarybchik	}
259283514Sarybchik
260283514Sarybchik	emr.emr_cmd = ip->u.mcdi.cmd;
261283514Sarybchik	emr.emr_in_buf = mcdibuf;
262283514Sarybchik	emr.emr_in_length = ip->u.mcdi.len;
263283514Sarybchik
264283514Sarybchik	emr.emr_out_buf = mcdibuf;
265283514Sarybchik	emr.emr_out_length = SFXGE_MCDI_MAX_PAYLOAD;
266283514Sarybchik
267283514Sarybchik	sfxge_mcdi_execute(sc, &emr);
268283514Sarybchik
269283514Sarybchik	ip->u.mcdi.rc = emr.emr_rc;
270283514Sarybchik	ip->u.mcdi.cmd = emr.emr_cmd;
271283514Sarybchik	ip->u.mcdi.len = emr.emr_out_length_used;
272283514Sarybchik	if ((rc = copyout(mcdibuf, ip->u.mcdi.payload, ip->u.mcdi.len)) != 0) {
273283514Sarybchik		goto fail6;
274283514Sarybchik	}
275283514Sarybchik
276283514Sarybchik	/*
277283514Sarybchik	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
278283514Sarybchik	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
279283514Sarybchik	 */
280283514Sarybchik	if (ip->u.mcdi.cmd == MC_CMD_REBOOT) {
281283514Sarybchik
282283514Sarybchik		EFSYS_PROBE(mcdi_ioctl_mc_reboot);
283283514Sarybchik		/* sfxge_t->s_state_lock held */
284283514Sarybchik		(void) sfxge_schedule_reset(sc);
285283514Sarybchik	}
286283514Sarybchik
287283514Sarybchik	free(mcdibuf, M_TEMP);
288283514Sarybchik
289283514Sarybchik	return (0);
290283514Sarybchik
291283514Sarybchikfail6:
292283514Sarybchikfail5:
293283514Sarybchik	free(mcdibuf, M_TEMP);
294283514Sarybchikfail3:
295283514Sarybchikfail2:
296283514Sarybchikfail1:
297283514Sarybchik	return (rc);
298283514Sarybchik}
299283514Sarybchik
300283514Sarybchik
301283514Sarybchikint
302227569Sphilipsfxge_mcdi_init(struct sfxge_softc *sc)
303227569Sphilip{
304227569Sphilip	efx_nic_t *enp;
305227569Sphilip	struct sfxge_mcdi *mcdi;
306227569Sphilip	efx_mcdi_transport_t *emtp;
307283514Sarybchik	efsys_mem_t *esmp;
308283514Sarybchik	int max_msg_size;
309227569Sphilip	int rc;
310227569Sphilip
311227569Sphilip	enp = sc->enp;
312227569Sphilip	mcdi = &sc->mcdi;
313227569Sphilip	emtp = &mcdi->transport;
314283514Sarybchik	esmp = &mcdi->mem;
315283514Sarybchik	max_msg_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
316227569Sphilip
317227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
318227569Sphilip	    ("MCDI already initialized"));
319227569Sphilip
320278250Sarybchik	SFXGE_MCDI_LOCK_INIT(mcdi, device_get_nameunit(sc->dev));
321227569Sphilip
322227569Sphilip	mcdi->state = SFXGE_MCDI_INITIALIZED;
323227569Sphilip
324283514Sarybchik	if ((rc = sfxge_dma_alloc(sc, max_msg_size, esmp)) != 0)
325283514Sarybchik		goto fail;
326283514Sarybchik
327227569Sphilip	emtp->emt_context = sc;
328283514Sarybchik	emtp->emt_dma_mem = esmp;
329227569Sphilip	emtp->emt_execute = sfxge_mcdi_execute;
330227569Sphilip	emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
331227569Sphilip	emtp->emt_exception = sfxge_mcdi_exception;
332291843Sarybchik#if EFSYS_OPT_MCDI_LOGGING
333291843Sarybchik	emtp->emt_logger = sfxge_mcdi_logger;
334291843Sarybchik	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev),
335291843Sarybchik		       SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
336291843Sarybchik		       OID_AUTO, "mcdi_logging", CTLFLAG_RW,
337291843Sarybchik		       &sc->mcdi_logging, 0,
338291843Sarybchik		       "MCDI logging");
339291843Sarybchik#endif
340227569Sphilip
341227569Sphilip	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
342227569Sphilip		goto fail;
343227569Sphilip
344227569Sphilip	return (0);
345227569Sphilip
346227569Sphilipfail:
347278221Sarybchik	SFXGE_MCDI_LOCK_DESTROY(mcdi);
348227569Sphilip	mcdi->state = SFXGE_MCDI_UNINITIALIZED;
349227569Sphilip	return (rc);
350227569Sphilip}
351227569Sphilip
352227569Sphilipvoid
353227569Sphilipsfxge_mcdi_fini(struct sfxge_softc *sc)
354227569Sphilip{
355227569Sphilip	struct sfxge_mcdi *mcdi;
356227569Sphilip	efx_nic_t *enp;
357227569Sphilip	efx_mcdi_transport_t *emtp;
358283514Sarybchik	efsys_mem_t *esmp;
359227569Sphilip
360227569Sphilip	enp = sc->enp;
361227569Sphilip	mcdi = &sc->mcdi;
362227569Sphilip	emtp = &mcdi->transport;
363283514Sarybchik	esmp = &mcdi->mem;
364227569Sphilip
365278221Sarybchik	SFXGE_MCDI_LOCK(mcdi);
366227569Sphilip	KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
367227569Sphilip	    ("MCDI not initialized"));
368227569Sphilip
369227569Sphilip	efx_mcdi_fini(enp);
370227569Sphilip	bzero(emtp, sizeof(*emtp));
371227569Sphilip
372278221Sarybchik	SFXGE_MCDI_UNLOCK(mcdi);
373227569Sphilip
374283514Sarybchik	sfxge_dma_free(esmp);
375283514Sarybchik
376278221Sarybchik	SFXGE_MCDI_LOCK_DESTROY(mcdi);
377227569Sphilip}
378