xref: /freebsd/sys/dev/sume/if_sume.c (revision fdafd315)
1bd368728SMarko Zec /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3bd368728SMarko Zec  *
4bd368728SMarko Zec  * Copyright (c) 2015 Bjoern A. Zeeb
5bd368728SMarko Zec  * Copyright (c) 2020 Denis Salopek
6bd368728SMarko Zec  *
7bd368728SMarko Zec  * This software was developed by SRI International and the University of
8bd368728SMarko Zec  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249
9bd368728SMarko Zec  * ("MRC2"), as part of the DARPA MRC research programme.
10bd368728SMarko Zec  *
11bd368728SMarko Zec  * Redistribution and use in source and binary forms, with or without
12bd368728SMarko Zec  * modification, are permitted provided that the following conditions
13bd368728SMarko Zec  * are met:
14bd368728SMarko Zec  * 1. Redistributions of source code must retain the above copyright
15bd368728SMarko Zec  *    notice, this list of conditions and the following disclaimer.
16bd368728SMarko Zec  * 2. Redistributions in binary form must reproduce the above copyright
17bd368728SMarko Zec  *    notice, this list of conditions and the following disclaimer in the
18bd368728SMarko Zec  *    documentation and/or other materials provided with the distribution.
19bd368728SMarko Zec  *
20bd368728SMarko Zec  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21bd368728SMarko Zec  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bd368728SMarko Zec  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23bd368728SMarko Zec  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24bd368728SMarko Zec  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25bd368728SMarko Zec  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26bd368728SMarko Zec  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27bd368728SMarko Zec  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28bd368728SMarko Zec  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29bd368728SMarko Zec  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30bd368728SMarko Zec  * POSSIBILITY OF SUCH DAMAGE.
31bd368728SMarko Zec  */
32bd368728SMarko Zec 
33bd368728SMarko Zec #include <sys/param.h>
34bd368728SMarko Zec #include <sys/bus.h>
35bd368728SMarko Zec #include <sys/endian.h>
36bd368728SMarko Zec #include <sys/kernel.h>
37bd368728SMarko Zec #include <sys/limits.h>
38bd368728SMarko Zec #include <sys/module.h>
39bd368728SMarko Zec #include <sys/rman.h>
40bd368728SMarko Zec #include <sys/socket.h>
41bd368728SMarko Zec #include <sys/sockio.h>
42bd368728SMarko Zec #include <sys/sysctl.h>
43bd368728SMarko Zec #include <sys/taskqueue.h>
44bd368728SMarko Zec 
45bd368728SMarko Zec #include <net/if.h>
46bd368728SMarko Zec #include <net/if_media.h>
47bd368728SMarko Zec #include <net/if_types.h>
48bd368728SMarko Zec #include <net/if_var.h>
49bd368728SMarko Zec 
50bd368728SMarko Zec #include <netinet/in.h>
51bd368728SMarko Zec #include <netinet/if_ether.h>
52bd368728SMarko Zec 
53bd368728SMarko Zec #include <dev/pci/pcivar.h>
54bd368728SMarko Zec #include <dev/pci/pcireg.h>
55bd368728SMarko Zec 
56bd368728SMarko Zec #include <machine/bus.h>
57bd368728SMarko Zec 
58bd368728SMarko Zec #include "adapter.h"
59bd368728SMarko Zec 
60bd368728SMarko Zec #define	PCI_VENDOR_ID_XILINX	0x10ee
61bd368728SMarko Zec #define	PCI_DEVICE_ID_SUME	0x7028
62bd368728SMarko Zec 
63bd368728SMarko Zec /* SUME bus driver interface */
64bd368728SMarko Zec static int sume_probe(device_t);
65bd368728SMarko Zec static int sume_attach(device_t);
66bd368728SMarko Zec static int sume_detach(device_t);
67bd368728SMarko Zec 
68bd368728SMarko Zec static device_method_t sume_methods[] = {
69bd368728SMarko Zec 	DEVMETHOD(device_probe,		sume_probe),
70bd368728SMarko Zec 	DEVMETHOD(device_attach,	sume_attach),
71bd368728SMarko Zec 	DEVMETHOD(device_detach,	sume_detach),
72bd368728SMarko Zec 	DEVMETHOD_END
73bd368728SMarko Zec };
74bd368728SMarko Zec 
75bd368728SMarko Zec static driver_t sume_driver = {
76bd368728SMarko Zec 	"sume",
77bd368728SMarko Zec 	sume_methods,
78bd368728SMarko Zec 	sizeof(struct sume_adapter)
79bd368728SMarko Zec };
80bd368728SMarko Zec 
81bd368728SMarko Zec /*
82bd368728SMarko Zec  * The DMA engine for SUME generates interrupts for each RX/TX transaction.
83bd368728SMarko Zec  * Depending on the channel (0 if packet transaction, 1 if register transaction)
84bd368728SMarko Zec  * the used bits of the interrupt vector will be the lowest or the second lowest
85bd368728SMarko Zec  * 5 bits.
86bd368728SMarko Zec  *
87bd368728SMarko Zec  * When receiving packets from SUME (RX):
88bd368728SMarko Zec  * (1) SUME received a packet on one of the interfaces.
89bd368728SMarko Zec  * (2) SUME generates an interrupt vector, bit 00001 is set (channel 0 - new RX
90bd368728SMarko Zec  *     transaction).
91bd368728SMarko Zec  * (3) We read the length of the incoming packet and the offset along with the
92bd368728SMarko Zec  *     'last' flag from the SUME registers.
93bd368728SMarko Zec  * (4) We prepare for the DMA transaction by setting the bouncebuffer on the
94bd368728SMarko Zec  *     address buf_addr. For now, this is how it's done:
95bd368728SMarko Zec  *     - First 3*sizeof(uint32_t) bytes are: lower and upper 32 bits of physical
96bd368728SMarko Zec  *     address where we want the data to arrive (buf_addr[0] and buf_addr[1]),
97bd368728SMarko Zec  *     and length of incoming data (buf_addr[2]).
98bd368728SMarko Zec  *     - Data will start right after, at buf_addr+3*sizeof(uint32_t). The
99bd368728SMarko Zec  *     physical address buf_hw_addr is a block of contiguous memory mapped to
100bd368728SMarko Zec  *     buf_addr, so we can set the incoming data's physical address (buf_addr[0]
101bd368728SMarko Zec  *     and buf_addr[1]) to buf_hw_addr+3*sizeof(uint32_t).
102bd368728SMarko Zec  * (5) We notify SUME that the bouncebuffer is ready for the transaction by
103bd368728SMarko Zec  *     writing the lower/upper physical address buf_hw_addr to the SUME
104bd368728SMarko Zec  *     registers RIFFA_TX_SG_ADDR_LO_REG_OFF and RIFFA_TX_SG_ADDR_HI_REG_OFF as
105bd368728SMarko Zec  *     well as the number of segments to the register RIFFA_TX_SG_LEN_REG_OFF.
106bd368728SMarko Zec  * (6) SUME generates an interrupt vector, bit 00010 is set (channel 0 -
107bd368728SMarko Zec  *     bouncebuffer received).
108bd368728SMarko Zec  * (7) SUME generates an interrupt vector, bit 00100 is set (channel 0 -
109bd368728SMarko Zec  *     transaction is done).
110bd368728SMarko Zec  * (8) SUME can do both steps (6) and (7) using the same interrupt.
111bd368728SMarko Zec  * (8) We read the first 16 bytes (metadata) of the received data and note the
112bd368728SMarko Zec  *     incoming interface so we can later forward it to the right one in the OS
113bd368728SMarko Zec  *     (sume0, sume1, sume2 or sume3).
114bd368728SMarko Zec  * (10) We create an mbuf and copy the data from the bouncebuffer to the mbuf
115bd368728SMarko Zec  *     and set the mbuf rcvif to the incoming interface.
116bd368728SMarko Zec  * (11) We forward the mbuf to the appropriate interface via ifp->if_input.
117bd368728SMarko Zec  *
118bd368728SMarko Zec  * When sending packets to SUME (TX):
119bd368728SMarko Zec  * (1) The OS calls sume_if_start() function on TX.
120bd368728SMarko Zec  * (2) We get the mbuf packet data and copy it to the
121bd368728SMarko Zec  *     buf_addr+3*sizeof(uint32_t) + metadata 16 bytes.
122bd368728SMarko Zec  * (3) We create the metadata based on the output interface and copy it to the
123bd368728SMarko Zec  *     buf_addr+3*sizeof(uint32_t).
124bd368728SMarko Zec  * (4) We write the offset/last and length of the packet to the SUME registers
125bd368728SMarko Zec  *     RIFFA_RX_OFFLAST_REG_OFF and RIFFA_RX_LEN_REG_OFF.
126bd368728SMarko Zec  * (5) We fill the bouncebuffer by filling the first 3*sizeof(uint32_t) bytes
127bd368728SMarko Zec  *     with the physical address and length just as in RX step (4).
128bd368728SMarko Zec  * (6) We notify SUME that the bouncebuffer is ready by writing to SUME
129bd368728SMarko Zec  *     registers RIFFA_RX_SG_ADDR_LO_REG_OFF, RIFFA_RX_SG_ADDR_HI_REG_OFF and
130bd368728SMarko Zec  *     RIFFA_RX_SG_LEN_REG_OFF just as in RX step (5).
131bd368728SMarko Zec  * (7) SUME generates an interrupt vector, bit 01000 is set (channel 0 -
132bd368728SMarko Zec  *     bouncebuffer is read).
133bd368728SMarko Zec  * (8) SUME generates an interrupt vector, bit 10000 is set (channel 0 -
134bd368728SMarko Zec  *     transaction is done).
135bd368728SMarko Zec  * (9) SUME can do both steps (7) and (8) using the same interrupt.
136bd368728SMarko Zec  *
137bd368728SMarko Zec  * Internal registers
138bd368728SMarko Zec  * Every module in the SUME hardware has its own set of internal registers
139bd368728SMarko Zec  * (IDs, for debugging and statistic purposes, etc.). Their base addresses are
140bd368728SMarko Zec  * defined in 'projects/reference_nic/hw/tcl/reference_nic_defines.tcl' and the
141bd368728SMarko Zec  * offsets to different memory locations of every module are defined in their
142bd368728SMarko Zec  * corresponding folder inside the library. These registers can be RO/RW and
143bd368728SMarko Zec  * there is a special method to fetch/change this data over 1 or 2 DMA
144bd368728SMarko Zec  * transactions. For writing, by calling the sume_module_reg_write(). For
145bd368728SMarko Zec  * reading, by calling the sume_module_reg_write() and then
146bd368728SMarko Zec  * sume_module_reg_read(). Check those functions for more information.
147bd368728SMarko Zec  */
148bd368728SMarko Zec 
149bd368728SMarko Zec MALLOC_DECLARE(M_SUME);
150bd368728SMarko Zec MALLOC_DEFINE(M_SUME, "sume", "NetFPGA SUME device driver");
151bd368728SMarko Zec 
152bd368728SMarko Zec static void check_tx_queues(struct sume_adapter *);
153bd368728SMarko Zec static void sume_fill_bb_desc(struct sume_adapter *, struct riffa_chnl_dir *,
154bd368728SMarko Zec     uint64_t);
155bd368728SMarko Zec 
156bd368728SMarko Zec static struct unrhdr *unr;
157bd368728SMarko Zec 
158bd368728SMarko Zec static struct {
159bd368728SMarko Zec 	uint16_t device;
160bd368728SMarko Zec 	char *desc;
161bd368728SMarko Zec } sume_pciids[] = {
162bd368728SMarko Zec 	{PCI_DEVICE_ID_SUME, "NetFPGA SUME reference NIC"},
163bd368728SMarko Zec };
164bd368728SMarko Zec 
165bd368728SMarko Zec static inline uint32_t
read_reg(struct sume_adapter * adapter,int offset)166bd368728SMarko Zec read_reg(struct sume_adapter *adapter, int offset)
167bd368728SMarko Zec {
168bd368728SMarko Zec 
169bd368728SMarko Zec 	return (bus_space_read_4(adapter->bt, adapter->bh, offset << 2));
170bd368728SMarko Zec }
171bd368728SMarko Zec 
172bd368728SMarko Zec static inline void
write_reg(struct sume_adapter * adapter,int offset,uint32_t val)173bd368728SMarko Zec write_reg(struct sume_adapter *adapter, int offset, uint32_t val)
174bd368728SMarko Zec {
175bd368728SMarko Zec 
176bd368728SMarko Zec 	bus_space_write_4(adapter->bt, adapter->bh, offset << 2, val);
177bd368728SMarko Zec }
178bd368728SMarko Zec 
179bd368728SMarko Zec static int
sume_probe(device_t dev)180bd368728SMarko Zec sume_probe(device_t dev)
181bd368728SMarko Zec {
182bd368728SMarko Zec 	int i;
183bd368728SMarko Zec 	uint16_t v = pci_get_vendor(dev);
184bd368728SMarko Zec 	uint16_t d = pci_get_device(dev);
185bd368728SMarko Zec 
186bd368728SMarko Zec 	if (v != PCI_VENDOR_ID_XILINX)
187bd368728SMarko Zec 		return (ENXIO);
188bd368728SMarko Zec 
189bd368728SMarko Zec 	for (i = 0; i < nitems(sume_pciids); i++) {
190bd368728SMarko Zec 		if (d == sume_pciids[i].device) {
191bd368728SMarko Zec 			device_set_desc(dev, sume_pciids[i].desc);
192bd368728SMarko Zec 			return (BUS_PROBE_DEFAULT);
193bd368728SMarko Zec 		}
194bd368728SMarko Zec 	}
195bd368728SMarko Zec 
196bd368728SMarko Zec 	return (ENXIO);
197bd368728SMarko Zec }
198bd368728SMarko Zec 
199bd368728SMarko Zec /*
200bd368728SMarko Zec  * Building mbuf for packet received from SUME. We expect to receive 'len'
201bd368728SMarko Zec  * bytes of data (including metadata) written from the bouncebuffer address
202bd368728SMarko Zec  * buf_addr+3*sizeof(uint32_t). Metadata will tell us which SUME interface
203bd368728SMarko Zec  * received the packet (sport will be 1, 2, 4 or 8), the packet length (plen),
204bd368728SMarko Zec  * and the magic word needs to be 0xcafe. When we have the packet data, we
205bd368728SMarko Zec  * create an mbuf and copy the data to it using m_copyback() function, set the
206bd368728SMarko Zec  * correct interface to rcvif and return the mbuf to be later sent to the OS
207bd368728SMarko Zec  * with if_input.
208bd368728SMarko Zec  */
209bd368728SMarko Zec static struct mbuf *
sume_rx_build_mbuf(struct sume_adapter * adapter,uint32_t len)210bd368728SMarko Zec sume_rx_build_mbuf(struct sume_adapter *adapter, uint32_t len)
211bd368728SMarko Zec {
212bd368728SMarko Zec 	struct nf_priv *nf_priv;
213bd368728SMarko Zec 	struct mbuf *m;
214cc970676SJustin Hibbits 	if_t ifp = NULL;
215bd368728SMarko Zec 	int np;
216bd368728SMarko Zec 	uint16_t dport, plen, magic;
217bd368728SMarko Zec 	device_t dev = adapter->dev;
218bd368728SMarko Zec 	uint8_t *indata = (uint8_t *)
219bd368728SMarko Zec 	    adapter->recv[SUME_RIFFA_CHANNEL_DATA]->buf_addr +
220bd368728SMarko Zec 	    sizeof(struct nf_bb_desc);
221bd368728SMarko Zec 	struct nf_metadata *mdata = (struct nf_metadata *) indata;
222bd368728SMarko Zec 
223bd368728SMarko Zec 	/* The metadata header is 16 bytes. */
224bd368728SMarko Zec 	if (len < sizeof(struct nf_metadata)) {
225bd368728SMarko Zec 		device_printf(dev, "short frame (%d)\n", len);
226bd368728SMarko Zec 		adapter->packets_err++;
227bd368728SMarko Zec 		adapter->bytes_err += len;
228bd368728SMarko Zec 		return (NULL);
229bd368728SMarko Zec 	}
230bd368728SMarko Zec 
231bd368728SMarko Zec 	dport = le16toh(mdata->dport);
232bd368728SMarko Zec 	plen = le16toh(mdata->plen);
233bd368728SMarko Zec 	magic = le16toh(mdata->magic);
234bd368728SMarko Zec 
235bd368728SMarko Zec 	if (sizeof(struct nf_metadata) + plen > len ||
236bd368728SMarko Zec 	    magic != SUME_RIFFA_MAGIC) {
237bd368728SMarko Zec 		device_printf(dev, "corrupted packet (%zd + %d > %d || magic "
238bd368728SMarko Zec 		    "0x%04x != 0x%04x)\n", sizeof(struct nf_metadata), plen,
239bd368728SMarko Zec 		    len, magic, SUME_RIFFA_MAGIC);
240bd368728SMarko Zec 		return (NULL);
241bd368728SMarko Zec 	}
242bd368728SMarko Zec 
243bd368728SMarko Zec 	/* We got the packet from one of the even bits */
244bd368728SMarko Zec 	np = (ffs(dport & SUME_DPORT_MASK) >> 1) - 1;
245bd368728SMarko Zec 	if (np > SUME_NPORTS) {
246bd368728SMarko Zec 		device_printf(dev, "invalid destination port 0x%04x (%d)\n",
247bd368728SMarko Zec 		    dport, np);
248bd368728SMarko Zec 		adapter->packets_err++;
249bd368728SMarko Zec 		adapter->bytes_err += plen;
250bd368728SMarko Zec 		return (NULL);
251bd368728SMarko Zec 	}
252bd368728SMarko Zec 	ifp = adapter->ifp[np];
253cc970676SJustin Hibbits 	nf_priv = if_getsoftc(ifp);
254bd368728SMarko Zec 	nf_priv->stats.rx_packets++;
255bd368728SMarko Zec 	nf_priv->stats.rx_bytes += plen;
256bd368728SMarko Zec 
257bd368728SMarko Zec 	/* If the interface is down, well, we are done. */
258cc970676SJustin Hibbits 	if (!(if_getflags(ifp) & IFF_UP)) {
259bd368728SMarko Zec 		nf_priv->stats.ifc_down_packets++;
260bd368728SMarko Zec 		nf_priv->stats.ifc_down_bytes += plen;
261bd368728SMarko Zec 		return (NULL);
262bd368728SMarko Zec 	}
263bd368728SMarko Zec 
264bd368728SMarko Zec 	if (adapter->sume_debug)
265bd368728SMarko Zec 		printf("Building mbuf with length: %d\n", plen);
266bd368728SMarko Zec 
267bd368728SMarko Zec 	m = m_getm(NULL, plen, M_NOWAIT, MT_DATA);
268bd368728SMarko Zec 	if (m == NULL) {
269bd368728SMarko Zec 		adapter->packets_err++;
270bd368728SMarko Zec 		adapter->bytes_err += plen;
271bd368728SMarko Zec 		return (NULL);
272bd368728SMarko Zec 	}
273bd368728SMarko Zec 
274bd368728SMarko Zec 	/* Copy the data in at the right offset. */
275bd368728SMarko Zec 	m_copyback(m, 0, plen, (void *) (indata + sizeof(struct nf_metadata)));
276bd368728SMarko Zec 	m->m_pkthdr.rcvif = ifp;
277bd368728SMarko Zec 
278bd368728SMarko Zec 	return (m);
279bd368728SMarko Zec }
280bd368728SMarko Zec 
281bd368728SMarko Zec /*
282bd368728SMarko Zec  * SUME interrupt handler for when we get a valid interrupt from the board.
283bd368728SMarko Zec  * Theoretically, we can receive interrupt for any of the available channels,
284bd368728SMarko Zec  * but RIFFA DMA uses only 2: 0 and 1, so we use only vect0. The vector is a 32
285bd368728SMarko Zec  * bit number, using 5 bits for every channel, the least significant bits
286bd368728SMarko Zec  * correspond to channel 0 and the next 5 bits correspond to channel 1. Vector
287bd368728SMarko Zec  * bits for RX/TX are:
288bd368728SMarko Zec  * RX
289bd368728SMarko Zec  * bit 0 - new transaction from SUME
290bd368728SMarko Zec  * bit 1 - SUME received our bouncebuffer address
291bd368728SMarko Zec  * bit 2 - SUME copied the received data to our bouncebuffer, transaction done
292bd368728SMarko Zec  * TX
293bd368728SMarko Zec  * bit 3 - SUME received our bouncebuffer address
294bd368728SMarko Zec  * bit 4 - SUME copied the data from our bouncebuffer, transaction done
295bd368728SMarko Zec  *
296bd368728SMarko Zec  * There are two finite state machines (one for TX, one for RX). We loop
297bd368728SMarko Zec  * through channels 0 and 1 to check and our current state and which interrupt
298bd368728SMarko Zec  * bit is set.
299bd368728SMarko Zec  * TX
300bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_IDLE: waiting for the first TX transaction.
301bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_READY: we prepared (filled with data) the bouncebuffer
302bd368728SMarko Zec  * and triggered the SUME for the TX transaction. Waiting for interrupt bit 3
303bd368728SMarko Zec  * to go to the next state.
304bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_READ: waiting for interrupt bit 4 (for SUME to send
305bd368728SMarko Zec  * our packet). Then we get the length of the sent data and go back to the
306bd368728SMarko Zec  * IDLE state.
307bd368728SMarko Zec  * RX
308bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_IDLE: waiting for the interrupt bit 0 (new RX
309bd368728SMarko Zec  * transaction). When we get it, we prepare our bouncebuffer for reading and
310bd368728SMarko Zec  * trigger the SUME to start the transaction. Go to the next state.
311bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_READY: waiting for the interrupt bit 1 (SUME got our
312bd368728SMarko Zec  * bouncebuffer). Go to the next state.
313bd368728SMarko Zec  * SUME_RIFFA_CHAN_STATE_READ: SUME copied data and our bouncebuffer is ready,
314bd368728SMarko Zec  * we can build the mbuf and go back to the IDLE state.
315bd368728SMarko Zec  */
316bd368728SMarko Zec static void
sume_intr_handler(void * arg)317bd368728SMarko Zec sume_intr_handler(void *arg)
318bd368728SMarko Zec {
319bd368728SMarko Zec 	struct sume_adapter *adapter = arg;
320bd368728SMarko Zec 	uint32_t vect, vect0, len;
321bd368728SMarko Zec 	int ch, loops;
322bd368728SMarko Zec 	device_t dev = adapter->dev;
323bd368728SMarko Zec 	struct mbuf *m = NULL;
324cc970676SJustin Hibbits 	if_t ifp = NULL;
325bd368728SMarko Zec 	struct riffa_chnl_dir *send, *recv;
326bd368728SMarko Zec 
327bd368728SMarko Zec 	SUME_LOCK(adapter);
328bd368728SMarko Zec 
329bd368728SMarko Zec 	vect0 = read_reg(adapter, RIFFA_IRQ_REG0_OFF);
330bd368728SMarko Zec 	if ((vect0 & SUME_INVALID_VECT) != 0) {
331bd368728SMarko Zec 		SUME_UNLOCK(adapter);
332bd368728SMarko Zec 		return;
333bd368728SMarko Zec 	}
334bd368728SMarko Zec 
335bd368728SMarko Zec 	/*
336bd368728SMarko Zec 	 * We only have one interrupt for all channels and no way
337bd368728SMarko Zec 	 * to quickly lookup for which channel(s) we got an interrupt?
338bd368728SMarko Zec 	 */
339bd368728SMarko Zec 	for (ch = 0; ch < SUME_RIFFA_CHANNELS; ch++) {
340bd368728SMarko Zec 		vect = vect0 >> (5 * ch);
341bd368728SMarko Zec 		send = adapter->send[ch];
342bd368728SMarko Zec 		recv = adapter->recv[ch];
343bd368728SMarko Zec 
344bd368728SMarko Zec 		loops = 0;
345bd368728SMarko Zec 		while ((vect & (SUME_MSI_TXBUF | SUME_MSI_TXDONE)) &&
346bd368728SMarko Zec 		    loops <= 5) {
347bd368728SMarko Zec 			if (adapter->sume_debug)
348bd368728SMarko Zec 				device_printf(dev, "TX ch %d state %u vect = "
349bd368728SMarko Zec 				    "0x%08x\n", ch, send->state, vect);
350bd368728SMarko Zec 			switch (send->state) {
351bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_IDLE:
352bd368728SMarko Zec 				break;
353bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_READY:
354bd368728SMarko Zec 				if (!(vect & SUME_MSI_TXBUF)) {
355bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
356bd368728SMarko Zec 					    "interrupt in send+3 state %u: "
357bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, send->state,
358bd368728SMarko Zec 					    vect);
359bd368728SMarko Zec 					send->recovery = 1;
360bd368728SMarko Zec 					break;
361bd368728SMarko Zec 				}
362bd368728SMarko Zec 				send->state = SUME_RIFFA_CHAN_STATE_READ;
363bd368728SMarko Zec 				vect &= ~SUME_MSI_TXBUF;
364bd368728SMarko Zec 				break;
365bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_READ:
366bd368728SMarko Zec 				if (!(vect & SUME_MSI_TXDONE)) {
367bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
368bd368728SMarko Zec 					    "interrupt in send+4 state %u: "
369bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, send->state,
370bd368728SMarko Zec 					    vect);
371bd368728SMarko Zec 					send->recovery = 1;
372bd368728SMarko Zec 					break;
373bd368728SMarko Zec 				}
374bd368728SMarko Zec 				send->state = SUME_RIFFA_CHAN_STATE_LEN;
375bd368728SMarko Zec 
376bd368728SMarko Zec 				len = read_reg(adapter, RIFFA_CHNL_REG(ch,
377bd368728SMarko Zec 				    RIFFA_RX_TNFR_LEN_REG_OFF));
378bd368728SMarko Zec 				if (ch == SUME_RIFFA_CHANNEL_DATA) {
379bd368728SMarko Zec 					send->state =
380bd368728SMarko Zec 					    SUME_RIFFA_CHAN_STATE_IDLE;
381bd368728SMarko Zec 					check_tx_queues(adapter);
382bd368728SMarko Zec 				} else if (ch == SUME_RIFFA_CHANNEL_REG)
383bd368728SMarko Zec 					wakeup(&send->event);
384bd368728SMarko Zec 				else {
385bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
386bd368728SMarko Zec 					    "interrupt in send+4 state %u: "
387bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, send->state,
388bd368728SMarko Zec 					    vect);
389bd368728SMarko Zec 					send->recovery = 1;
390bd368728SMarko Zec 				}
391bd368728SMarko Zec 				vect &= ~SUME_MSI_TXDONE;
392bd368728SMarko Zec 				break;
393bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_LEN:
394bd368728SMarko Zec 				break;
395bd368728SMarko Zec 			default:
396bd368728SMarko Zec 				device_printf(dev, "unknown TX state!\n");
397bd368728SMarko Zec 			}
398bd368728SMarko Zec 			loops++;
399bd368728SMarko Zec 		}
400bd368728SMarko Zec 
401bd368728SMarko Zec 		if ((vect & (SUME_MSI_TXBUF | SUME_MSI_TXDONE)) &&
402bd368728SMarko Zec 		    send->recovery)
403bd368728SMarko Zec 			device_printf(dev, "ch %d ignoring vect = 0x%08x "
404bd368728SMarko Zec 			    "during TX; not in recovery; state = %d loops = "
405bd368728SMarko Zec 			    "%d\n", ch, vect, send->state, loops);
406bd368728SMarko Zec 
407bd368728SMarko Zec 		loops = 0;
408bd368728SMarko Zec 		while ((vect & (SUME_MSI_RXQUE | SUME_MSI_RXBUF |
409bd368728SMarko Zec 		    SUME_MSI_RXDONE)) && loops < 5) {
410bd368728SMarko Zec 			if (adapter->sume_debug)
411bd368728SMarko Zec 				device_printf(dev, "RX ch %d state %u vect = "
412bd368728SMarko Zec 				    "0x%08x\n", ch, recv->state, vect);
413bd368728SMarko Zec 			switch (recv->state) {
414bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_IDLE:
415bd368728SMarko Zec 				if (!(vect & SUME_MSI_RXQUE)) {
416bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
417bd368728SMarko Zec 					    "interrupt in recv+0 state %u: "
418bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, recv->state,
419bd368728SMarko Zec 					    vect);
420bd368728SMarko Zec 					recv->recovery = 1;
421bd368728SMarko Zec 					break;
422bd368728SMarko Zec 				}
423bd368728SMarko Zec 				uint32_t max_ptr;
424bd368728SMarko Zec 
425bd368728SMarko Zec 				/* Clear recovery state. */
426bd368728SMarko Zec 				recv->recovery = 0;
427bd368728SMarko Zec 
428bd368728SMarko Zec 				/* Get offset and length. */
429bd368728SMarko Zec 				recv->offlast = read_reg(adapter,
430bd368728SMarko Zec 				    RIFFA_CHNL_REG(ch,
431bd368728SMarko Zec 				    RIFFA_TX_OFFLAST_REG_OFF));
432bd368728SMarko Zec 				recv->len = read_reg(adapter, RIFFA_CHNL_REG(ch,
433bd368728SMarko Zec 				    RIFFA_TX_LEN_REG_OFF));
434bd368728SMarko Zec 
435bd368728SMarko Zec 				/* Boundary checks. */
436bd368728SMarko Zec 				max_ptr = (uint32_t)((uintptr_t)recv->buf_addr
437bd368728SMarko Zec 				    + SUME_RIFFA_OFFSET(recv->offlast)
438bd368728SMarko Zec 				    + SUME_RIFFA_LEN(recv->len) - 1);
439bd368728SMarko Zec 				if (max_ptr <
440bd368728SMarko Zec 				    (uint32_t)((uintptr_t)recv->buf_addr))
441bd368728SMarko Zec 					device_printf(dev, "receive buffer "
442bd368728SMarko Zec 					    "wrap-around overflow.\n");
443bd368728SMarko Zec 				if (SUME_RIFFA_OFFSET(recv->offlast) +
444bd368728SMarko Zec 				    SUME_RIFFA_LEN(recv->len) >
445bd368728SMarko Zec 				    adapter->sg_buf_size)
446bd368728SMarko Zec 					device_printf(dev, "receive buffer too"
447bd368728SMarko Zec 					    " small.\n");
448bd368728SMarko Zec 
449bd368728SMarko Zec 				/* Fill the bouncebuf "descriptor". */
450bd368728SMarko Zec 				sume_fill_bb_desc(adapter, recv,
451bd368728SMarko Zec 				    SUME_RIFFA_LEN(recv->len));
452bd368728SMarko Zec 
453bd368728SMarko Zec 				bus_dmamap_sync(recv->ch_tag, recv->ch_map,
454bd368728SMarko Zec 				    BUS_DMASYNC_PREREAD |
455bd368728SMarko Zec 				    BUS_DMASYNC_PREWRITE);
456bd368728SMarko Zec 				write_reg(adapter, RIFFA_CHNL_REG(ch,
457bd368728SMarko Zec 				    RIFFA_TX_SG_ADDR_LO_REG_OFF),
458bd368728SMarko Zec 				    SUME_RIFFA_LO_ADDR(recv->buf_hw_addr));
459bd368728SMarko Zec 				write_reg(adapter, RIFFA_CHNL_REG(ch,
460bd368728SMarko Zec 				    RIFFA_TX_SG_ADDR_HI_REG_OFF),
461bd368728SMarko Zec 				    SUME_RIFFA_HI_ADDR(recv->buf_hw_addr));
462bd368728SMarko Zec 				write_reg(adapter, RIFFA_CHNL_REG(ch,
463bd368728SMarko Zec 				    RIFFA_TX_SG_LEN_REG_OFF),
464bd368728SMarko Zec 				    4 * recv->num_sg);
465bd368728SMarko Zec 				bus_dmamap_sync(recv->ch_tag, recv->ch_map,
466bd368728SMarko Zec 				    BUS_DMASYNC_POSTREAD |
467bd368728SMarko Zec 				    BUS_DMASYNC_POSTWRITE);
468bd368728SMarko Zec 
469bd368728SMarko Zec 				recv->state = SUME_RIFFA_CHAN_STATE_READY;
470bd368728SMarko Zec 				vect &= ~SUME_MSI_RXQUE;
471bd368728SMarko Zec 				break;
472bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_READY:
473bd368728SMarko Zec 				if (!(vect & SUME_MSI_RXBUF)) {
474bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
475bd368728SMarko Zec 					    "interrupt in recv+1 state %u: "
476bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, recv->state,
477bd368728SMarko Zec 					    vect);
478bd368728SMarko Zec 					recv->recovery = 1;
479bd368728SMarko Zec 					break;
480bd368728SMarko Zec 				}
481bd368728SMarko Zec 				recv->state = SUME_RIFFA_CHAN_STATE_READ;
482bd368728SMarko Zec 				vect &= ~SUME_MSI_RXBUF;
483bd368728SMarko Zec 				break;
484bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_READ:
485bd368728SMarko Zec 				if (!(vect & SUME_MSI_RXDONE)) {
486bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
487bd368728SMarko Zec 					    "interrupt in recv+2 state %u: "
488bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, recv->state,
489bd368728SMarko Zec 					    vect);
490bd368728SMarko Zec 					recv->recovery = 1;
491bd368728SMarko Zec 					break;
492bd368728SMarko Zec 				}
493bd368728SMarko Zec 				len = read_reg(adapter, RIFFA_CHNL_REG(ch,
494bd368728SMarko Zec 				    RIFFA_TX_TNFR_LEN_REG_OFF));
495bd368728SMarko Zec 
496bd368728SMarko Zec 				/* Remember, len and recv->len are words. */
497bd368728SMarko Zec 				if (ch == SUME_RIFFA_CHANNEL_DATA) {
498bd368728SMarko Zec 					m = sume_rx_build_mbuf(adapter,
499bd368728SMarko Zec 					    len << 2);
500bd368728SMarko Zec 					recv->state =
501bd368728SMarko Zec 					    SUME_RIFFA_CHAN_STATE_IDLE;
502bd368728SMarko Zec 				} else if (ch == SUME_RIFFA_CHANNEL_REG)
503bd368728SMarko Zec 					wakeup(&recv->event);
504bd368728SMarko Zec 				else {
505bd368728SMarko Zec 					device_printf(dev, "ch %d unexpected "
506bd368728SMarko Zec 					    "interrupt in recv+2 state %u: "
507bd368728SMarko Zec 					    "vect = 0x%08x\n", ch, recv->state,
508bd368728SMarko Zec 					    vect);
509bd368728SMarko Zec 					recv->recovery = 1;
510bd368728SMarko Zec 				}
511bd368728SMarko Zec 				vect &= ~SUME_MSI_RXDONE;
512bd368728SMarko Zec 				break;
513bd368728SMarko Zec 			case SUME_RIFFA_CHAN_STATE_LEN:
514bd368728SMarko Zec 				break;
515bd368728SMarko Zec 			default:
516bd368728SMarko Zec 				device_printf(dev, "unknown RX state!\n");
517bd368728SMarko Zec 			}
518bd368728SMarko Zec 			loops++;
519bd368728SMarko Zec 		}
520bd368728SMarko Zec 
521bd368728SMarko Zec 		if ((vect & (SUME_MSI_RXQUE | SUME_MSI_RXBUF |
522bd368728SMarko Zec 		    SUME_MSI_RXDONE)) && recv->recovery) {
523bd368728SMarko Zec 			device_printf(dev, "ch %d ignoring vect = 0x%08x "
524bd368728SMarko Zec 			    "during RX; not in recovery; state = %d, loops = "
525bd368728SMarko Zec 			    "%d\n", ch, vect, recv->state, loops);
526bd368728SMarko Zec 
527bd368728SMarko Zec 			/* Clean the unfinished transaction. */
528bd368728SMarko Zec 			if (ch == SUME_RIFFA_CHANNEL_REG &&
529bd368728SMarko Zec 			    vect & SUME_MSI_RXDONE) {
530bd368728SMarko Zec 				read_reg(adapter, RIFFA_CHNL_REG(ch,
531bd368728SMarko Zec 				    RIFFA_TX_TNFR_LEN_REG_OFF));
532bd368728SMarko Zec 				recv->recovery = 0;
533bd368728SMarko Zec 			}
534bd368728SMarko Zec 		}
535bd368728SMarko Zec 	}
536bd368728SMarko Zec 	SUME_UNLOCK(adapter);
537bd368728SMarko Zec 
538bd368728SMarko Zec 	if (m != NULL) {
539bd368728SMarko Zec 		ifp = m->m_pkthdr.rcvif;
540cc970676SJustin Hibbits 		if_input(ifp, m);
541bd368728SMarko Zec 	}
542bd368728SMarko Zec }
543bd368728SMarko Zec 
544bd368728SMarko Zec /*
545bd368728SMarko Zec  * As we cannot disable interrupt generation, ignore early interrupts by waiting
546bd368728SMarko Zec  * for the adapter to go into the 'running' state.
547bd368728SMarko Zec  */
548bd368728SMarko Zec static int
sume_intr_filter(void * arg)549bd368728SMarko Zec sume_intr_filter(void *arg)
550bd368728SMarko Zec {
551bd368728SMarko Zec 	struct sume_adapter *adapter = arg;
552bd368728SMarko Zec 
553bd368728SMarko Zec 	if (adapter->running == 0)
554bd368728SMarko Zec 		return (FILTER_STRAY);
555bd368728SMarko Zec 
556bd368728SMarko Zec 	return (FILTER_SCHEDULE_THREAD);
557bd368728SMarko Zec }
558bd368728SMarko Zec 
559bd368728SMarko Zec static int
sume_probe_riffa_pci(struct sume_adapter * adapter)560bd368728SMarko Zec sume_probe_riffa_pci(struct sume_adapter *adapter)
561bd368728SMarko Zec {
562bd368728SMarko Zec 	device_t dev = adapter->dev;
563bd368728SMarko Zec 	int error, count, capmem;
564bd368728SMarko Zec 	uint32_t reg, devctl, linkctl;
565bd368728SMarko Zec 
566bd368728SMarko Zec 	pci_enable_busmaster(dev);
567bd368728SMarko Zec 
568bd368728SMarko Zec 	adapter->rid = PCIR_BAR(0);
569bd368728SMarko Zec 	adapter->bar0_addr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
570bd368728SMarko Zec 	    &adapter->rid, RF_ACTIVE);
571bd368728SMarko Zec 	if (adapter->bar0_addr == NULL) {
572bd368728SMarko Zec 		device_printf(dev, "unable to allocate bus resource: "
573bd368728SMarko Zec 		    "BAR0 address\n");
574bd368728SMarko Zec 		return (ENXIO);
575bd368728SMarko Zec 	}
576bd368728SMarko Zec 	adapter->bt = rman_get_bustag(adapter->bar0_addr);
577bd368728SMarko Zec 	adapter->bh = rman_get_bushandle(adapter->bar0_addr);
578bd368728SMarko Zec 	adapter->bar0_len = rman_get_size(adapter->bar0_addr);
579bd368728SMarko Zec 	if (adapter->bar0_len != 1024) {
580bd368728SMarko Zec 		device_printf(dev, "BAR0 resource length %lu != 1024\n",
581bd368728SMarko Zec 		    adapter->bar0_len);
582bd368728SMarko Zec 		return (ENXIO);
583bd368728SMarko Zec 	}
584bd368728SMarko Zec 
585bd368728SMarko Zec 	count = pci_msi_count(dev);
586bd368728SMarko Zec 	error = pci_alloc_msi(dev, &count);
587bd368728SMarko Zec 	if (error) {
588bd368728SMarko Zec 		device_printf(dev, "unable to allocate bus resource: PCI "
589bd368728SMarko Zec 		    "MSI\n");
590bd368728SMarko Zec 		return (error);
591bd368728SMarko Zec 	}
592bd368728SMarko Zec 
593bd368728SMarko Zec 	adapter->irq.rid = 1; /* Should be 1, thus says pci_alloc_msi() */
594bd368728SMarko Zec 	adapter->irq.res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
595bd368728SMarko Zec 	    &adapter->irq.rid, RF_SHAREABLE | RF_ACTIVE);
596bd368728SMarko Zec 	if (adapter->irq.res == NULL) {
597bd368728SMarko Zec 		device_printf(dev, "unable to allocate bus resource: IRQ "
598bd368728SMarko Zec 		    "memory\n");
599bd368728SMarko Zec 		return (ENXIO);
600bd368728SMarko Zec 	}
601bd368728SMarko Zec 
602bd368728SMarko Zec 	error = bus_setup_intr(dev, adapter->irq.res, INTR_MPSAFE |
603bd368728SMarko Zec 	    INTR_TYPE_NET, sume_intr_filter, sume_intr_handler, adapter,
604bd368728SMarko Zec 	    &adapter->irq.tag);
605bd368728SMarko Zec 	if (error) {
606bd368728SMarko Zec 		device_printf(dev, "failed to setup interrupt for rid %d, name"
607bd368728SMarko Zec 		    " %s: %d\n", adapter->irq.rid, "SUME_INTR", error);
608bd368728SMarko Zec 		return (ENXIO);
609bd368728SMarko Zec 	}
610bd368728SMarko Zec 
611bd368728SMarko Zec 	if (pci_find_cap(dev, PCIY_EXPRESS, &capmem) != 0) {
612bd368728SMarko Zec 		device_printf(dev, "PCI not PCIe capable\n");
613bd368728SMarko Zec 		return (ENXIO);
614bd368728SMarko Zec 	}
615bd368728SMarko Zec 
616bd368728SMarko Zec 	devctl = pci_read_config(dev, capmem + PCIER_DEVICE_CTL, 2);
617bd368728SMarko Zec 	pci_write_config(dev, capmem + PCIER_DEVICE_CTL, (devctl |
618bd368728SMarko Zec 	    PCIEM_CTL_EXT_TAG_FIELD), 2);
619bd368728SMarko Zec 
620bd368728SMarko Zec 	devctl = pci_read_config(dev, capmem + PCIER_DEVICE_CTL2, 2);
621bd368728SMarko Zec 	pci_write_config(dev, capmem + PCIER_DEVICE_CTL2, (devctl |
622bd368728SMarko Zec 	    PCIEM_CTL2_ID_ORDERED_REQ_EN), 2);
623bd368728SMarko Zec 
624bd368728SMarko Zec 	linkctl = pci_read_config(dev, capmem + PCIER_LINK_CTL, 2);
625bd368728SMarko Zec 	pci_write_config(dev, capmem + PCIER_LINK_CTL, (linkctl |
626bd368728SMarko Zec 	    PCIEM_LINK_CTL_RCB), 2);
627bd368728SMarko Zec 
628bd368728SMarko Zec 	reg = read_reg(adapter, RIFFA_INFO_REG_OFF);
629bd368728SMarko Zec 	adapter->num_sg = RIFFA_SG_ELEMS * ((reg >> 19) & 0xf);
630bd368728SMarko Zec 	adapter->sg_buf_size = RIFFA_SG_BUF_SIZE * ((reg >> 19) & 0xf);
631bd368728SMarko Zec 
632bd368728SMarko Zec 	error = ENODEV;
633bd368728SMarko Zec 	/* Check bus master is enabled. */
634bd368728SMarko Zec 	if (((reg >> 4) & 0x1) != 1) {
635bd368728SMarko Zec 		device_printf(dev, "bus master not enabled: %d\n",
636bd368728SMarko Zec 		    (reg >> 4) & 0x1);
637bd368728SMarko Zec 		return (error);
638bd368728SMarko Zec 	}
639bd368728SMarko Zec 	/* Check link parameters are valid. */
640bd368728SMarko Zec 	if (((reg >> 5) & 0x3f) == 0 || ((reg >> 11) & 0x3) == 0) {
641bd368728SMarko Zec 		device_printf(dev, "link parameters not valid: %d %d\n",
642bd368728SMarko Zec 		    (reg >> 5) & 0x3f, (reg >> 11) & 0x3);
643bd368728SMarko Zec 		return (error);
644bd368728SMarko Zec 	}
645bd368728SMarko Zec 	/* Check # of channels are within valid range. */
646bd368728SMarko Zec 	if ((reg & 0xf) == 0 || (reg & 0xf) > RIFFA_MAX_CHNLS) {
647bd368728SMarko Zec 		device_printf(dev, "number of channels out of range: %d\n",
648bd368728SMarko Zec 		    reg & 0xf);
649bd368728SMarko Zec 		return (error);
650bd368728SMarko Zec 	}
651bd368728SMarko Zec 	/* Check bus width. */
652bd368728SMarko Zec 	if (((reg >> 19) & 0xf) == 0 ||
653bd368728SMarko Zec 	    ((reg >> 19) & 0xf) > RIFFA_MAX_BUS_WIDTH_PARAM) {
654bd368728SMarko Zec 		device_printf(dev, "bus width out of range: %d\n",
655bd368728SMarko Zec 		    (reg >> 19) & 0xf);
656bd368728SMarko Zec 		return (error);
657bd368728SMarko Zec 	}
658bd368728SMarko Zec 
659bd368728SMarko Zec 	device_printf(dev, "[riffa] # of channels: %d\n",
660bd368728SMarko Zec 	    reg & 0xf);
661bd368728SMarko Zec 	device_printf(dev, "[riffa] bus interface width: %d\n",
662bd368728SMarko Zec 	    ((reg >> 19) & 0xf) << 5);
663bd368728SMarko Zec 	device_printf(dev, "[riffa] bus master enabled: %d\n",
664bd368728SMarko Zec 	    (reg >> 4) & 0x1);
665bd368728SMarko Zec 	device_printf(dev, "[riffa] negotiated link width: %d\n",
666bd368728SMarko Zec 	    (reg >> 5) & 0x3f);
667bd368728SMarko Zec 	device_printf(dev, "[riffa] negotiated rate width: %d MTs\n",
668bd368728SMarko Zec 	    ((reg >> 11) & 0x3) * 2500);
669bd368728SMarko Zec 	device_printf(dev, "[riffa] max downstream payload: %d B\n",
670bd368728SMarko Zec 	    128 << ((reg >> 13) & 0x7));
671bd368728SMarko Zec 	device_printf(dev, "[riffa] max upstream payload: %d B\n",
672bd368728SMarko Zec 	    128 << ((reg >> 16) & 0x7));
673bd368728SMarko Zec 
674bd368728SMarko Zec 	return (0);
675bd368728SMarko Zec }
676bd368728SMarko Zec 
677bd368728SMarko Zec /* If there is no sume_if_init, the ether_ioctl panics. */
678bd368728SMarko Zec static void
sume_if_init(void * sc)679bd368728SMarko Zec sume_if_init(void *sc)
680bd368728SMarko Zec {
681bd368728SMarko Zec }
682bd368728SMarko Zec 
683bd368728SMarko Zec /* Write the address and length for our incoming / outgoing transaction. */
684bd368728SMarko Zec static void
sume_fill_bb_desc(struct sume_adapter * adapter,struct riffa_chnl_dir * p,uint64_t len)685bd368728SMarko Zec sume_fill_bb_desc(struct sume_adapter *adapter, struct riffa_chnl_dir *p,
686bd368728SMarko Zec     uint64_t len)
687bd368728SMarko Zec {
688bd368728SMarko Zec 	struct nf_bb_desc *bouncebuf = (struct nf_bb_desc *) p->buf_addr;
689bd368728SMarko Zec 
690bd368728SMarko Zec 	bouncebuf->lower = (p->buf_hw_addr + sizeof(struct nf_bb_desc));
691bd368728SMarko Zec 	bouncebuf->upper = (p->buf_hw_addr + sizeof(struct nf_bb_desc)) >> 32;
692bd368728SMarko Zec 	bouncebuf->len = len >> 2;
693bd368728SMarko Zec }
694bd368728SMarko Zec 
695bd368728SMarko Zec /* Module register locked write. */
696bd368728SMarko Zec static int
sume_modreg_write_locked(struct sume_adapter * adapter)697bd368728SMarko Zec sume_modreg_write_locked(struct sume_adapter *adapter)
698bd368728SMarko Zec {
699bd368728SMarko Zec 	struct riffa_chnl_dir *send = adapter->send[SUME_RIFFA_CHANNEL_REG];
700bd368728SMarko Zec 
701bd368728SMarko Zec 	/* Let the FPGA know about the transfer. */
702bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_REG,
703bd368728SMarko Zec 	    RIFFA_RX_OFFLAST_REG_OFF), SUME_OFFLAST);
704bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_REG,
705bd368728SMarko Zec 	    RIFFA_RX_LEN_REG_OFF), send->len);	/* words */
706bd368728SMarko Zec 
707bd368728SMarko Zec 	/* Fill the bouncebuf "descriptor". */
708bd368728SMarko Zec 	sume_fill_bb_desc(adapter, send, SUME_RIFFA_LEN(send->len));
709bd368728SMarko Zec 
710bd368728SMarko Zec 	/* Update the state before intiating the DMA to avoid races. */
711bd368728SMarko Zec 	send->state = SUME_RIFFA_CHAN_STATE_READY;
712bd368728SMarko Zec 
713bd368728SMarko Zec 	bus_dmamap_sync(send->ch_tag, send->ch_map,
714bd368728SMarko Zec 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
715bd368728SMarko Zec 	/* DMA. */
716bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_REG,
717bd368728SMarko Zec 	    RIFFA_RX_SG_ADDR_LO_REG_OFF),
718bd368728SMarko Zec 	    SUME_RIFFA_LO_ADDR(send->buf_hw_addr));
719bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_REG,
720bd368728SMarko Zec 	    RIFFA_RX_SG_ADDR_HI_REG_OFF),
721bd368728SMarko Zec 	    SUME_RIFFA_HI_ADDR(send->buf_hw_addr));
722bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_REG,
723bd368728SMarko Zec 	    RIFFA_RX_SG_LEN_REG_OFF), 4 * send->num_sg);
724bd368728SMarko Zec 	bus_dmamap_sync(send->ch_tag, send->ch_map,
725bd368728SMarko Zec 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
726bd368728SMarko Zec 
727bd368728SMarko Zec 	return (0);
728bd368728SMarko Zec }
729bd368728SMarko Zec 
730bd368728SMarko Zec /*
731bd368728SMarko Zec  * Request a register read or write (depending on optype).
732bd368728SMarko Zec  * If optype is set (0x1f) this will result in a register write,
733bd368728SMarko Zec  * otherwise this will result in a register read request at the given
734bd368728SMarko Zec  * address and the result will need to be DMAed back.
735bd368728SMarko Zec  */
736bd368728SMarko Zec static int
sume_module_reg_write(struct nf_priv * nf_priv,struct sume_ifreq * sifr,uint32_t optype)737bd368728SMarko Zec sume_module_reg_write(struct nf_priv *nf_priv, struct sume_ifreq *sifr,
738bd368728SMarko Zec     uint32_t optype)
739bd368728SMarko Zec {
740bd368728SMarko Zec 	struct sume_adapter *adapter = nf_priv->adapter;
741bd368728SMarko Zec 	struct riffa_chnl_dir *send = adapter->send[SUME_RIFFA_CHANNEL_REG];
742bd368728SMarko Zec 	struct nf_regop_data *data;
743bd368728SMarko Zec 	int error;
744bd368728SMarko Zec 
745bd368728SMarko Zec 	/*
746bd368728SMarko Zec 	 * 1. Make sure the channel is free;  otherwise return EBUSY.
747bd368728SMarko Zec 	 * 2. Prepare the memory in the bounce buffer (which we always
748bd368728SMarko Zec 	 *    use for regs).
749bd368728SMarko Zec 	 * 3. Start the DMA process.
750bd368728SMarko Zec 	 * 4. Sleep and wait for result and return success or error.
751bd368728SMarko Zec 	 */
752bd368728SMarko Zec 	SUME_LOCK(adapter);
753bd368728SMarko Zec 
754bd368728SMarko Zec 	if (send->state != SUME_RIFFA_CHAN_STATE_IDLE) {
755bd368728SMarko Zec 		SUME_UNLOCK(adapter);
756bd368728SMarko Zec 		return (EBUSY);
757bd368728SMarko Zec 	}
758bd368728SMarko Zec 
759bd368728SMarko Zec 	data = (struct nf_regop_data *) (send->buf_addr +
760bd368728SMarko Zec 	    sizeof(struct nf_bb_desc));
761bd368728SMarko Zec 	data->addr = htole32(sifr->addr);
762bd368728SMarko Zec 	data->val = htole32(sifr->val);
763bd368728SMarko Zec 	/* Tag to indentify request. */
764bd368728SMarko Zec 	data->rtag = htole32(++send->rtag);
765bd368728SMarko Zec 	data->optype = htole32(optype);
766bd368728SMarko Zec 	send->len = sizeof(struct nf_regop_data) / 4; /* words */
767bd368728SMarko Zec 
768bd368728SMarko Zec 	error = sume_modreg_write_locked(adapter);
769bd368728SMarko Zec 	if (error) {
770bd368728SMarko Zec 		SUME_UNLOCK(adapter);
771bd368728SMarko Zec 		return (EFAULT);
772bd368728SMarko Zec 	}
773bd368728SMarko Zec 
774bd368728SMarko Zec 	/* Timeout after 1s. */
775bd368728SMarko Zec 	if (send->state != SUME_RIFFA_CHAN_STATE_LEN)
776bd368728SMarko Zec 		error = msleep(&send->event, &adapter->lock, 0,
777bd368728SMarko Zec 		    "Waiting recv finish", 1 * hz);
778bd368728SMarko Zec 
779bd368728SMarko Zec 	/* This was a write so we are done; were interrupted, or timed out. */
780bd368728SMarko Zec 	if (optype != SUME_MR_READ || error != 0 || error == EWOULDBLOCK) {
781bd368728SMarko Zec 		send->state = SUME_RIFFA_CHAN_STATE_IDLE;
782bd368728SMarko Zec 		if (optype == SUME_MR_READ)
783bd368728SMarko Zec 			error = EWOULDBLOCK;
784bd368728SMarko Zec 		else
785bd368728SMarko Zec 			error = 0;
786bd368728SMarko Zec 	} else
787bd368728SMarko Zec 		error = 0;
788bd368728SMarko Zec 
789bd368728SMarko Zec 	/*
790bd368728SMarko Zec 	 * For read requests we will update state once we are done
791bd368728SMarko Zec 	 * having read the result to avoid any two outstanding
792bd368728SMarko Zec 	 * transactions, or we need a queue and validate tags,
793bd368728SMarko Zec 	 * which is a lot of work for a low priority, infrequent
794bd368728SMarko Zec 	 * event.
795bd368728SMarko Zec 	 */
796bd368728SMarko Zec 
797bd368728SMarko Zec 	SUME_UNLOCK(adapter);
798bd368728SMarko Zec 
799bd368728SMarko Zec 	return (error);
800bd368728SMarko Zec }
801bd368728SMarko Zec 
802bd368728SMarko Zec /* Module register read. */
803bd368728SMarko Zec static int
sume_module_reg_read(struct nf_priv * nf_priv,struct sume_ifreq * sifr)804bd368728SMarko Zec sume_module_reg_read(struct nf_priv *nf_priv, struct sume_ifreq *sifr)
805bd368728SMarko Zec {
806bd368728SMarko Zec 	struct sume_adapter *adapter = nf_priv->adapter;
807bd368728SMarko Zec 	struct riffa_chnl_dir *recv = adapter->recv[SUME_RIFFA_CHANNEL_REG];
808bd368728SMarko Zec 	struct riffa_chnl_dir *send = adapter->send[SUME_RIFFA_CHANNEL_REG];
809bd368728SMarko Zec 	struct nf_regop_data *data;
810bd368728SMarko Zec 	int error = 0;
811bd368728SMarko Zec 
812bd368728SMarko Zec 	/*
813bd368728SMarko Zec 	 * 0. Sleep waiting for result if needed (unless condition is
814bd368728SMarko Zec 	 *    true already).
815bd368728SMarko Zec 	 * 1. Read DMA results.
816bd368728SMarko Zec 	 * 2. Update state on *TX* to IDLE to allow next read to start.
817bd368728SMarko Zec 	 */
818bd368728SMarko Zec 	SUME_LOCK(adapter);
819bd368728SMarko Zec 
820bd368728SMarko Zec 	bus_dmamap_sync(recv->ch_tag, recv->ch_map,
821bd368728SMarko Zec 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
822bd368728SMarko Zec 	/*
823bd368728SMarko Zec 	 * We only need to be woken up at the end of the transaction.
824bd368728SMarko Zec 	 * Timeout after 1s.
825bd368728SMarko Zec 	 */
826bd368728SMarko Zec 	if (recv->state != SUME_RIFFA_CHAN_STATE_READ)
827bd368728SMarko Zec 		error = msleep(&recv->event, &adapter->lock, 0,
828bd368728SMarko Zec 		    "Waiting transaction finish", 1 * hz);
829bd368728SMarko Zec 
830bd368728SMarko Zec 	if (recv->state != SUME_RIFFA_CHAN_STATE_READ || error == EWOULDBLOCK) {
831bd368728SMarko Zec 		SUME_UNLOCK(adapter);
832bd368728SMarko Zec 		device_printf(adapter->dev, "wait error: %d\n", error);
833bd368728SMarko Zec 		return (EWOULDBLOCK);
834bd368728SMarko Zec 	}
835bd368728SMarko Zec 
836bd368728SMarko Zec 	bus_dmamap_sync(recv->ch_tag, recv->ch_map,
837bd368728SMarko Zec 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
838bd368728SMarko Zec 
839bd368728SMarko Zec 	/*
840bd368728SMarko Zec 	 * Read reply data and validate address and tag.
841bd368728SMarko Zec 	 * Note: we do access the send side without lock but the state
842bd368728SMarko Zec 	 * machine does prevent the data from changing.
843bd368728SMarko Zec 	 */
844bd368728SMarko Zec 	data = (struct nf_regop_data *) (recv->buf_addr +
845bd368728SMarko Zec 	    sizeof(struct nf_bb_desc));
846bd368728SMarko Zec 
847bd368728SMarko Zec 	if (le32toh(data->rtag) != send->rtag)
848bd368728SMarko Zec 		device_printf(adapter->dev, "rtag error: 0x%08x 0x%08x\n",
849bd368728SMarko Zec 		    le32toh(data->rtag), send->rtag);
850bd368728SMarko Zec 
851bd368728SMarko Zec 	sifr->val = le32toh(data->val);
852bd368728SMarko Zec 	recv->state = SUME_RIFFA_CHAN_STATE_IDLE;
853bd368728SMarko Zec 
854bd368728SMarko Zec 	/* We are done. */
855bd368728SMarko Zec 	send->state = SUME_RIFFA_CHAN_STATE_IDLE;
856bd368728SMarko Zec 
857bd368728SMarko Zec 	SUME_UNLOCK(adapter);
858bd368728SMarko Zec 
859bd368728SMarko Zec 	return (0);
860bd368728SMarko Zec }
861bd368728SMarko Zec 
862bd368728SMarko Zec /* Read value from a module register and return it to a sume_ifreq. */
863bd368728SMarko Zec static int
get_modreg_value(struct nf_priv * nf_priv,struct sume_ifreq * sifr)864bd368728SMarko Zec get_modreg_value(struct nf_priv *nf_priv, struct sume_ifreq *sifr)
865bd368728SMarko Zec {
866bd368728SMarko Zec 	int error;
867bd368728SMarko Zec 
868bd368728SMarko Zec 	error = sume_module_reg_write(nf_priv, sifr, SUME_MR_READ);
869bd368728SMarko Zec 	if (!error)
870bd368728SMarko Zec 		error = sume_module_reg_read(nf_priv, sifr);
871bd368728SMarko Zec 
872bd368728SMarko Zec 	return (error);
873bd368728SMarko Zec }
874bd368728SMarko Zec 
875bd368728SMarko Zec static int
sume_if_ioctl(if_t ifp,unsigned long cmd,caddr_t data)876cc970676SJustin Hibbits sume_if_ioctl(if_t ifp, unsigned long cmd, caddr_t data)
877bd368728SMarko Zec {
878bd368728SMarko Zec 	struct ifreq *ifr = (struct ifreq *) data;
879cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
880bd368728SMarko Zec 	struct sume_ifreq sifr;
881bd368728SMarko Zec 	int error = 0;
882bd368728SMarko Zec 
883bd368728SMarko Zec 	switch (cmd) {
884bd368728SMarko Zec 	case SIOCGIFMEDIA:
885bd368728SMarko Zec 	case SIOCGIFXMEDIA:
886bd368728SMarko Zec 		error = ifmedia_ioctl(ifp, ifr, &nf_priv->media, cmd);
887bd368728SMarko Zec 		break;
888bd368728SMarko Zec 
889bd368728SMarko Zec 	case SUME_IOCTL_CMD_WRITE_REG:
890bd368728SMarko Zec 		error = copyin(ifr_data_get_ptr(ifr), &sifr, sizeof(sifr));
891bd368728SMarko Zec 		if (error) {
892bd368728SMarko Zec 			error = EINVAL;
893bd368728SMarko Zec 			break;
894bd368728SMarko Zec 		}
895bd368728SMarko Zec 		error = sume_module_reg_write(nf_priv, &sifr, SUME_MR_WRITE);
896bd368728SMarko Zec 		break;
897bd368728SMarko Zec 
898bd368728SMarko Zec 	case SUME_IOCTL_CMD_READ_REG:
899bd368728SMarko Zec 		error = copyin(ifr_data_get_ptr(ifr), &sifr, sizeof(sifr));
900bd368728SMarko Zec 		if (error) {
901bd368728SMarko Zec 			error = EINVAL;
902bd368728SMarko Zec 			break;
903bd368728SMarko Zec 		}
904bd368728SMarko Zec 
905bd368728SMarko Zec 		error = get_modreg_value(nf_priv, &sifr);
906bd368728SMarko Zec 		if (error)
907bd368728SMarko Zec 			break;
908bd368728SMarko Zec 
909bd368728SMarko Zec 		error = copyout(&sifr, ifr_data_get_ptr(ifr), sizeof(sifr));
910bd368728SMarko Zec 		if (error)
911bd368728SMarko Zec 			error = EINVAL;
912bd368728SMarko Zec 
913bd368728SMarko Zec 		break;
914bd368728SMarko Zec 
915bd368728SMarko Zec 	case SIOCSIFFLAGS:
916bd368728SMarko Zec 		/* Silence tcpdump 'promisc mode not supported' warning. */
917cc970676SJustin Hibbits 		if (if_getflags(ifp) & IFF_PROMISC)
918bd368728SMarko Zec 			break;
919bd368728SMarko Zec 
920bd368728SMarko Zec 	default:
921bd368728SMarko Zec 		error = ether_ioctl(ifp, cmd, data);
922bd368728SMarko Zec 		break;
923bd368728SMarko Zec 	}
924bd368728SMarko Zec 
925bd368728SMarko Zec 	return (error);
926bd368728SMarko Zec }
927bd368728SMarko Zec 
928bd368728SMarko Zec static int
sume_media_change(if_t ifp)929cc970676SJustin Hibbits sume_media_change(if_t ifp)
930bd368728SMarko Zec {
931cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
932bd368728SMarko Zec 	struct ifmedia *ifm = &nf_priv->media;
933bd368728SMarko Zec 
934bd368728SMarko Zec 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
935bd368728SMarko Zec 		return (EINVAL);
936bd368728SMarko Zec 
937bd368728SMarko Zec 	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10G_SR)
938cc970676SJustin Hibbits 		if_setbaudrate(ifp, ifmedia_baudrate(IFM_ETHER | IFM_10G_SR));
939bd368728SMarko Zec 	else
940cc970676SJustin Hibbits 		if_setbaudrate(ifp, ifmedia_baudrate(ifm->ifm_media));
941bd368728SMarko Zec 
942bd368728SMarko Zec 	return (0);
943bd368728SMarko Zec }
944bd368728SMarko Zec 
945bd368728SMarko Zec static void
sume_update_link_status(if_t ifp)946cc970676SJustin Hibbits sume_update_link_status(if_t ifp)
947bd368728SMarko Zec {
948cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
949bd368728SMarko Zec 	struct sume_adapter *adapter = nf_priv->adapter;
950bd368728SMarko Zec 	struct sume_ifreq sifr;
951bd368728SMarko Zec 	int link_status;
952bd368728SMarko Zec 
953bd368728SMarko Zec 	sifr.addr = SUME_STATUS_ADDR(nf_priv->port);
954bd368728SMarko Zec 	sifr.val = 0;
955bd368728SMarko Zec 
956bd368728SMarko Zec 	if (get_modreg_value(nf_priv, &sifr))
957bd368728SMarko Zec 		return;
958bd368728SMarko Zec 
959bd368728SMarko Zec 	link_status = SUME_LINK_STATUS(sifr.val);
960bd368728SMarko Zec 
961bd368728SMarko Zec 	if (!link_status && nf_priv->link_up) {
962bd368728SMarko Zec 		if_link_state_change(ifp, LINK_STATE_DOWN);
963bd368728SMarko Zec 		nf_priv->link_up = 0;
964bd368728SMarko Zec 		if (adapter->sume_debug)
965bd368728SMarko Zec 			device_printf(adapter->dev, "port %d link state "
966bd368728SMarko Zec 			    "changed to DOWN\n", nf_priv->unit);
967bd368728SMarko Zec 	} else if (link_status && !nf_priv->link_up) {
968bd368728SMarko Zec 		nf_priv->link_up = 1;
969bd368728SMarko Zec 		if_link_state_change(ifp, LINK_STATE_UP);
970bd368728SMarko Zec 		if (adapter->sume_debug)
971bd368728SMarko Zec 			device_printf(adapter->dev, "port %d link state "
972bd368728SMarko Zec 			    "changed to UP\n", nf_priv->unit);
973bd368728SMarko Zec 	}
974bd368728SMarko Zec }
975bd368728SMarko Zec 
976bd368728SMarko Zec static void
sume_media_status(if_t ifp,struct ifmediareq * ifmr)977cc970676SJustin Hibbits sume_media_status(if_t ifp, struct ifmediareq *ifmr)
978bd368728SMarko Zec {
979cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
980bd368728SMarko Zec 	struct ifmedia *ifm = &nf_priv->media;
981bd368728SMarko Zec 
982bd368728SMarko Zec 	if (ifm->ifm_cur->ifm_media == (IFM_ETHER | IFM_10G_SR) &&
983cc970676SJustin Hibbits 	    (if_getflags(ifp) & IFF_UP))
984bd368728SMarko Zec 		ifmr->ifm_active = IFM_ETHER | IFM_10G_SR;
985bd368728SMarko Zec 	else
986bd368728SMarko Zec 		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
987bd368728SMarko Zec 
988bd368728SMarko Zec 	ifmr->ifm_status |= IFM_AVALID;
989bd368728SMarko Zec 
990bd368728SMarko Zec 	sume_update_link_status(ifp);
991bd368728SMarko Zec 
992bd368728SMarko Zec 	if (nf_priv->link_up)
993bd368728SMarko Zec 		ifmr->ifm_status |= IFM_ACTIVE;
994bd368728SMarko Zec }
995bd368728SMarko Zec 
996bd368728SMarko Zec /*
997bd368728SMarko Zec  * Packet to transmit. We take the packet data from the mbuf and copy it to the
998bd368728SMarko Zec  * bouncebuffer address buf_addr+3*sizeof(uint32_t)+16. The 16 bytes before the
999bd368728SMarko Zec  * packet data are for metadata: sport/dport (depending on our source
1000bd368728SMarko Zec  * interface), packet length and magic 0xcafe. We tell the SUME about the
1001bd368728SMarko Zec  * transfer, fill the first 3*sizeof(uint32_t) bytes of the bouncebuffer with
1002bd368728SMarko Zec  * the information about the start and length of the packet and trigger the
1003bd368728SMarko Zec  * transaction.
1004bd368728SMarko Zec  */
1005bd368728SMarko Zec static int
sume_if_start_locked(if_t ifp)1006cc970676SJustin Hibbits sume_if_start_locked(if_t ifp)
1007bd368728SMarko Zec {
1008bd368728SMarko Zec 	struct mbuf *m;
1009cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
1010bd368728SMarko Zec 	struct sume_adapter *adapter = nf_priv->adapter;
1011bd368728SMarko Zec 	struct riffa_chnl_dir *send = adapter->send[SUME_RIFFA_CHANNEL_DATA];
1012bd368728SMarko Zec 	uint8_t *outbuf;
1013bd368728SMarko Zec 	struct nf_metadata *mdata;
1014bd368728SMarko Zec 	int plen = SUME_MIN_PKT_SIZE;
1015bd368728SMarko Zec 
1016bd368728SMarko Zec 	KASSERT(mtx_owned(&adapter->lock), ("SUME lock not owned"));
1017bd368728SMarko Zec 	KASSERT(send->state == SUME_RIFFA_CHAN_STATE_IDLE,
1018bd368728SMarko Zec 	    ("SUME not in IDLE state"));
1019bd368728SMarko Zec 
1020cc970676SJustin Hibbits 	m = if_dequeue(ifp);
1021bd368728SMarko Zec 	if (m == NULL)
1022bd368728SMarko Zec 		return (EINVAL);
1023bd368728SMarko Zec 
1024bd368728SMarko Zec 	/* Packets large enough do not need to be padded */
1025bd368728SMarko Zec 	if (m->m_pkthdr.len > SUME_MIN_PKT_SIZE)
1026bd368728SMarko Zec 		plen = m->m_pkthdr.len;
1027bd368728SMarko Zec 
1028bd368728SMarko Zec 	if (adapter->sume_debug)
1029bd368728SMarko Zec 		device_printf(adapter->dev, "sending %d bytes to %s%d\n", plen,
1030bd368728SMarko Zec 		    SUME_ETH_DEVICE_NAME, nf_priv->unit);
1031bd368728SMarko Zec 
1032bd368728SMarko Zec 	outbuf = (uint8_t *) send->buf_addr + sizeof(struct nf_bb_desc);
1033bd368728SMarko Zec 	mdata = (struct nf_metadata *) outbuf;
1034bd368728SMarko Zec 
1035bd368728SMarko Zec 	/* Clear the recovery flag. */
1036bd368728SMarko Zec 	send->recovery = 0;
1037bd368728SMarko Zec 
1038bd368728SMarko Zec 	/* Make sure we fit with the 16 bytes nf_metadata. */
1039bd368728SMarko Zec 	if (m->m_pkthdr.len + sizeof(struct nf_metadata) >
1040bd368728SMarko Zec 	    adapter->sg_buf_size) {
1041bd368728SMarko Zec 		device_printf(adapter->dev, "packet too big for bounce buffer "
1042bd368728SMarko Zec 		    "(%d)\n", m->m_pkthdr.len);
1043bd368728SMarko Zec 		m_freem(m);
1044bd368728SMarko Zec 		nf_priv->stats.tx_dropped++;
1045bd368728SMarko Zec 		return (ENOMEM);
1046bd368728SMarko Zec 	}
1047bd368728SMarko Zec 
1048bd368728SMarko Zec 	bus_dmamap_sync(send->ch_tag, send->ch_map,
1049bd368728SMarko Zec 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1050bd368728SMarko Zec 
1051bd368728SMarko Zec 	/* Zero out the padded data */
1052bd368728SMarko Zec 	if (m->m_pkthdr.len < SUME_MIN_PKT_SIZE)
1053bd368728SMarko Zec 		bzero(outbuf + sizeof(struct nf_metadata), SUME_MIN_PKT_SIZE);
1054bd368728SMarko Zec 	/* Skip the first 16 bytes for the metadata. */
1055bd368728SMarko Zec 	m_copydata(m, 0, m->m_pkthdr.len, outbuf + sizeof(struct nf_metadata));
1056bd368728SMarko Zec 	send->len = (sizeof(struct nf_metadata) + plen + 3) / 4;
1057bd368728SMarko Zec 
1058bd368728SMarko Zec 	/* Fill in the metadata: CPU(DMA) ports are odd, MAC ports are even. */
1059bd368728SMarko Zec 	mdata->sport = htole16(1 << (nf_priv->port * 2 + 1));
1060bd368728SMarko Zec 	mdata->dport = htole16(1 << (nf_priv->port * 2));
1061bd368728SMarko Zec 	mdata->plen = htole16(plen);
1062bd368728SMarko Zec 	mdata->magic = htole16(SUME_RIFFA_MAGIC);
1063bd368728SMarko Zec 	mdata->t1 = htole32(0);
1064bd368728SMarko Zec 	mdata->t2 = htole32(0);
1065bd368728SMarko Zec 
1066bd368728SMarko Zec 	/* Let the FPGA know about the transfer. */
1067bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_DATA,
1068bd368728SMarko Zec 	    RIFFA_RX_OFFLAST_REG_OFF), SUME_OFFLAST);
1069bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_DATA,
1070bd368728SMarko Zec 	    RIFFA_RX_LEN_REG_OFF), send->len);
1071bd368728SMarko Zec 
1072bd368728SMarko Zec 	/* Fill the bouncebuf "descriptor". */
1073bd368728SMarko Zec 	sume_fill_bb_desc(adapter, send, SUME_RIFFA_LEN(send->len));
1074bd368728SMarko Zec 
1075bd368728SMarko Zec 	/* Update the state before intiating the DMA to avoid races. */
1076bd368728SMarko Zec 	send->state = SUME_RIFFA_CHAN_STATE_READY;
1077bd368728SMarko Zec 
1078bd368728SMarko Zec 	/* DMA. */
1079bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_DATA,
1080bd368728SMarko Zec 	    RIFFA_RX_SG_ADDR_LO_REG_OFF),
1081bd368728SMarko Zec 	    SUME_RIFFA_LO_ADDR(send->buf_hw_addr));
1082bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_DATA,
1083bd368728SMarko Zec 	    RIFFA_RX_SG_ADDR_HI_REG_OFF),
1084bd368728SMarko Zec 	    SUME_RIFFA_HI_ADDR(send->buf_hw_addr));
1085bd368728SMarko Zec 	write_reg(adapter, RIFFA_CHNL_REG(SUME_RIFFA_CHANNEL_DATA,
1086bd368728SMarko Zec 	    RIFFA_RX_SG_LEN_REG_OFF), 4 * send->num_sg);
1087bd368728SMarko Zec 
1088bd368728SMarko Zec 	bus_dmamap_sync(send->ch_tag, send->ch_map,
1089bd368728SMarko Zec 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1090bd368728SMarko Zec 
1091bd368728SMarko Zec 	nf_priv->stats.tx_packets++;
1092bd368728SMarko Zec 	nf_priv->stats.tx_bytes += plen;
1093bd368728SMarko Zec 
1094bd368728SMarko Zec 	/* We can free as long as we use the bounce buffer. */
1095bd368728SMarko Zec 	m_freem(m);
1096bd368728SMarko Zec 
1097bd368728SMarko Zec 	adapter->last_ifc = nf_priv->port;
1098bd368728SMarko Zec 
1099bd368728SMarko Zec 	/* Reset watchdog counter. */
1100bd368728SMarko Zec 	adapter->wd_counter = 0;
1101bd368728SMarko Zec 
1102bd368728SMarko Zec 	return (0);
1103bd368728SMarko Zec }
1104bd368728SMarko Zec 
1105bd368728SMarko Zec static void
sume_if_start(if_t ifp)1106cc970676SJustin Hibbits sume_if_start(if_t ifp)
1107bd368728SMarko Zec {
1108cc970676SJustin Hibbits 	struct nf_priv *nf_priv = if_getsoftc(ifp);
1109bd368728SMarko Zec 	struct sume_adapter *adapter = nf_priv->adapter;
1110bd368728SMarko Zec 
1111cc970676SJustin Hibbits 	if (!adapter->running || !(if_getflags(ifp) & IFF_UP))
1112bd368728SMarko Zec 		return;
1113bd368728SMarko Zec 
1114bd368728SMarko Zec 	SUME_LOCK(adapter);
1115bd368728SMarko Zec 	if (adapter->send[SUME_RIFFA_CHANNEL_DATA]->state ==
1116bd368728SMarko Zec 	    SUME_RIFFA_CHAN_STATE_IDLE)
1117bd368728SMarko Zec 		sume_if_start_locked(ifp);
1118bd368728SMarko Zec 	SUME_UNLOCK(adapter);
1119bd368728SMarko Zec }
1120bd368728SMarko Zec 
1121bd368728SMarko Zec /*
1122bd368728SMarko Zec  * We call this function at the end of every TX transaction to check for
1123bd368728SMarko Zec  * remaining packets in the TX queues for every UP interface.
1124bd368728SMarko Zec  */
1125bd368728SMarko Zec static void
check_tx_queues(struct sume_adapter * adapter)1126bd368728SMarko Zec check_tx_queues(struct sume_adapter *adapter)
1127bd368728SMarko Zec {
1128bd368728SMarko Zec 	int i, last_ifc;
1129bd368728SMarko Zec 
1130bd368728SMarko Zec 	KASSERT(mtx_owned(&adapter->lock), ("SUME lock not owned"));
1131bd368728SMarko Zec 
1132bd368728SMarko Zec 	last_ifc = adapter->last_ifc;
1133bd368728SMarko Zec 
1134bd368728SMarko Zec 	/* Check all interfaces */
1135bd368728SMarko Zec 	for (i = last_ifc + 1; i < last_ifc + SUME_NPORTS + 1; i++) {
1136cc970676SJustin Hibbits 		if_t ifp = adapter->ifp[i % SUME_NPORTS];
1137bd368728SMarko Zec 
1138cc970676SJustin Hibbits 		if (!(if_getflags(ifp) & IFF_UP))
1139bd368728SMarko Zec 			continue;
1140bd368728SMarko Zec 
1141bd368728SMarko Zec 		if (!sume_if_start_locked(ifp))
1142bd368728SMarko Zec 			break;
1143bd368728SMarko Zec 	}
1144bd368728SMarko Zec }
1145bd368728SMarko Zec 
1146bd368728SMarko Zec static int
sume_ifp_alloc(struct sume_adapter * adapter,uint32_t port)1147bd368728SMarko Zec sume_ifp_alloc(struct sume_adapter *adapter, uint32_t port)
1148bd368728SMarko Zec {
1149cc970676SJustin Hibbits 	if_t ifp;
1150bd368728SMarko Zec 	struct nf_priv *nf_priv = malloc(sizeof(struct nf_priv), M_SUME,
1151bd368728SMarko Zec 	    M_ZERO | M_WAITOK);
1152bd368728SMarko Zec 
1153bd368728SMarko Zec 	ifp = if_alloc(IFT_ETHER);
1154bd368728SMarko Zec 	if (ifp == NULL) {
1155bd368728SMarko Zec 		device_printf(adapter->dev, "cannot allocate ifnet\n");
1156bd368728SMarko Zec 		return (ENOMEM);
1157bd368728SMarko Zec 	}
1158bd368728SMarko Zec 
1159bd368728SMarko Zec 	adapter->ifp[port] = ifp;
1160cc970676SJustin Hibbits 	if_setsoftc(ifp, nf_priv);
1161bd368728SMarko Zec 
1162bd368728SMarko Zec 	nf_priv->adapter = adapter;
1163bd368728SMarko Zec 	nf_priv->unit = alloc_unr(unr);
1164bd368728SMarko Zec 	nf_priv->port = port;
1165bd368728SMarko Zec 	nf_priv->link_up = 0;
1166bd368728SMarko Zec 
1167bd368728SMarko Zec 	if_initname(ifp, SUME_ETH_DEVICE_NAME, nf_priv->unit);
1168cc970676SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
1169bd368728SMarko Zec 
1170cc970676SJustin Hibbits 	if_setinitfn(ifp, sume_if_init);
1171cc970676SJustin Hibbits 	if_setstartfn(ifp, sume_if_start);
1172cc970676SJustin Hibbits 	if_setioctlfn(ifp, sume_if_ioctl);
1173bd368728SMarko Zec 
1174bd368728SMarko Zec 	uint8_t hw_addr[ETHER_ADDR_LEN] = DEFAULT_ETHER_ADDRESS;
1175bd368728SMarko Zec 	hw_addr[ETHER_ADDR_LEN-1] = nf_priv->unit;
1176bd368728SMarko Zec 	ether_ifattach(ifp, hw_addr);
1177bd368728SMarko Zec 
1178bd368728SMarko Zec 	ifmedia_init(&nf_priv->media, IFM_IMASK, sume_media_change,
1179bd368728SMarko Zec 	    sume_media_status);
1180bd368728SMarko Zec 	ifmedia_add(&nf_priv->media, IFM_ETHER | IFM_10G_SR, 0, NULL);
1181bd368728SMarko Zec 	ifmedia_set(&nf_priv->media, IFM_ETHER | IFM_10G_SR);
1182bd368728SMarko Zec 
1183cc970676SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
1184bd368728SMarko Zec 
1185bd368728SMarko Zec 	return (0);
1186bd368728SMarko Zec }
1187bd368728SMarko Zec 
1188bd368728SMarko Zec static void
callback_dma(void * arg,bus_dma_segment_t * segs,int nseg,int err)1189bd368728SMarko Zec callback_dma(void *arg, bus_dma_segment_t *segs, int nseg, int err)
1190bd368728SMarko Zec {
1191bd368728SMarko Zec 	if (err)
1192bd368728SMarko Zec 		return;
1193bd368728SMarko Zec 
1194bd368728SMarko Zec 	KASSERT(nseg == 1, ("%d segments returned!", nseg));
1195bd368728SMarko Zec 
1196bd368728SMarko Zec 	*(bus_addr_t *) arg = segs[0].ds_addr;
1197bd368728SMarko Zec }
1198bd368728SMarko Zec 
1199bd368728SMarko Zec static int
sume_probe_riffa_buffer(const struct sume_adapter * adapter,struct riffa_chnl_dir *** p,const char * dir)1200bd368728SMarko Zec sume_probe_riffa_buffer(const struct sume_adapter *adapter,
1201bd368728SMarko Zec     struct riffa_chnl_dir ***p, const char *dir)
1202bd368728SMarko Zec {
1203bd368728SMarko Zec 	struct riffa_chnl_dir **rp;
1204bd368728SMarko Zec 	bus_addr_t hw_addr;
1205bd368728SMarko Zec 	int error, ch;
1206bd368728SMarko Zec 	device_t dev = adapter->dev;
1207bd368728SMarko Zec 
1208bd368728SMarko Zec 	error = ENOMEM;
1209bd368728SMarko Zec 	*p = malloc(SUME_RIFFA_CHANNELS * sizeof(struct riffa_chnl_dir *),
1210bd368728SMarko Zec 	    M_SUME, M_ZERO | M_WAITOK);
1211bd368728SMarko Zec 	if (*p == NULL) {
1212bd368728SMarko Zec 		device_printf(dev, "malloc(%s) failed.\n", dir);
1213bd368728SMarko Zec 		return (error);
1214bd368728SMarko Zec 	}
1215bd368728SMarko Zec 
1216bd368728SMarko Zec 	rp = *p;
1217bd368728SMarko Zec 	/* Allocate the chnl_dir structs themselves. */
1218bd368728SMarko Zec 	for (ch = 0; ch < SUME_RIFFA_CHANNELS; ch++) {
1219bd368728SMarko Zec 		/* One direction. */
1220bd368728SMarko Zec 		rp[ch] = malloc(sizeof(struct riffa_chnl_dir), M_SUME,
1221bd368728SMarko Zec 		    M_ZERO | M_WAITOK);
1222bd368728SMarko Zec 		if (rp[ch] == NULL) {
1223bd368728SMarko Zec 			device_printf(dev, "malloc(%s[%d]) riffa_chnl_dir "
1224bd368728SMarko Zec 			    "failed.\n", dir, ch);
1225bd368728SMarko Zec 			return (error);
1226bd368728SMarko Zec 		}
1227bd368728SMarko Zec 
1228bd368728SMarko Zec 		int err = bus_dma_tag_create(bus_get_dma_tag(dev),
1229bd368728SMarko Zec 		    4, 0,
1230bd368728SMarko Zec 		    BUS_SPACE_MAXADDR,
1231bd368728SMarko Zec 		    BUS_SPACE_MAXADDR,
1232bd368728SMarko Zec 		    NULL, NULL,
1233bd368728SMarko Zec 		    adapter->sg_buf_size,
1234bd368728SMarko Zec 		    1,
1235bd368728SMarko Zec 		    adapter->sg_buf_size,
1236bd368728SMarko Zec 		    0,
1237bd368728SMarko Zec 		    NULL,
1238bd368728SMarko Zec 		    NULL,
1239bd368728SMarko Zec 		    &rp[ch]->ch_tag);
1240bd368728SMarko Zec 
1241bd368728SMarko Zec 		if (err) {
1242bd368728SMarko Zec 			device_printf(dev, "bus_dma_tag_create(%s[%d]) "
1243bd368728SMarko Zec 			    "failed.\n", dir, ch);
1244bd368728SMarko Zec 			return (err);
1245bd368728SMarko Zec 		}
1246bd368728SMarko Zec 
1247bd368728SMarko Zec 		err = bus_dmamem_alloc(rp[ch]->ch_tag, (void **)
1248bd368728SMarko Zec 		    &rp[ch]->buf_addr, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
1249bd368728SMarko Zec 		    BUS_DMA_ZERO, &rp[ch]->ch_map);
1250bd368728SMarko Zec 		if (err) {
1251bd368728SMarko Zec 			device_printf(dev, "bus_dmamem_alloc(%s[%d]) failed.\n",
1252bd368728SMarko Zec 			    dir, ch);
1253bd368728SMarko Zec 			return (err);
1254bd368728SMarko Zec 		}
1255bd368728SMarko Zec 
1256bd368728SMarko Zec 		bzero(rp[ch]->buf_addr, adapter->sg_buf_size);
1257bd368728SMarko Zec 
1258bd368728SMarko Zec 		err = bus_dmamap_load(rp[ch]->ch_tag, rp[ch]->ch_map,
1259bd368728SMarko Zec 		    rp[ch]->buf_addr, adapter->sg_buf_size, callback_dma,
1260bd368728SMarko Zec 		    &hw_addr, BUS_DMA_NOWAIT);
1261bd368728SMarko Zec 		if (err) {
1262bd368728SMarko Zec 			device_printf(dev, "bus_dmamap_load(%s[%d]) failed.\n",
1263bd368728SMarko Zec 			    dir, ch);
1264bd368728SMarko Zec 			return (err);
1265bd368728SMarko Zec 		}
1266bd368728SMarko Zec 		rp[ch]->buf_hw_addr = hw_addr;
1267bd368728SMarko Zec 		rp[ch]->num_sg = 1;
1268bd368728SMarko Zec 		rp[ch]->state = SUME_RIFFA_CHAN_STATE_IDLE;
1269bd368728SMarko Zec 
1270bd368728SMarko Zec 		rp[ch]->rtag = SUME_INIT_RTAG;
1271bd368728SMarko Zec 	}
1272bd368728SMarko Zec 
1273bd368728SMarko Zec 	return (0);
1274bd368728SMarko Zec }
1275bd368728SMarko Zec 
1276bd368728SMarko Zec static int
sume_probe_riffa_buffers(struct sume_adapter * adapter)1277bd368728SMarko Zec sume_probe_riffa_buffers(struct sume_adapter *adapter)
1278bd368728SMarko Zec {
1279bd368728SMarko Zec 	int error;
1280bd368728SMarko Zec 
1281bd368728SMarko Zec 	error = sume_probe_riffa_buffer(adapter, &adapter->recv, "recv");
1282bd368728SMarko Zec 	if (error)
1283bd368728SMarko Zec 		return (error);
1284bd368728SMarko Zec 
1285bd368728SMarko Zec 	error = sume_probe_riffa_buffer(adapter, &adapter->send, "send");
1286bd368728SMarko Zec 
1287bd368728SMarko Zec 	return (error);
1288bd368728SMarko Zec }
1289bd368728SMarko Zec 
1290bd368728SMarko Zec static void
sume_sysctl_init(struct sume_adapter * adapter)1291bd368728SMarko Zec sume_sysctl_init(struct sume_adapter *adapter)
1292bd368728SMarko Zec {
1293bd368728SMarko Zec 	device_t dev = adapter->dev;
1294bd368728SMarko Zec 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
1295bd368728SMarko Zec 	struct sysctl_oid *tree = device_get_sysctl_tree(dev);
1296bd368728SMarko Zec 	struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
1297bd368728SMarko Zec 	struct sysctl_oid *tmp_tree;
1298bd368728SMarko Zec 	char namebuf[MAX_IFC_NAME_LEN];
1299bd368728SMarko Zec 	int i;
1300bd368728SMarko Zec 
1301bd368728SMarko Zec 	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "sume", CTLFLAG_RW,
1302bd368728SMarko Zec 	    0, "SUME top-level tree");
1303bd368728SMarko Zec 	if (tree == NULL) {
1304bd368728SMarko Zec 		device_printf(dev, "SYSCTL_ADD_NODE failed.\n");
1305bd368728SMarko Zec 		return;
1306bd368728SMarko Zec 	}
1307bd368728SMarko Zec 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug", CTLFLAG_RW,
1308bd368728SMarko Zec 	    &adapter->sume_debug, 0, "debug int leaf");
1309bd368728SMarko Zec 
1310bd368728SMarko Zec 	/* total RX error stats */
1311bd368728SMarko Zec 	SYSCTL_ADD_U64(ctx, child, OID_AUTO, "rx_epkts",
1312bd368728SMarko Zec 	    CTLFLAG_RD, &adapter->packets_err, 0, "rx errors");
1313bd368728SMarko Zec 	SYSCTL_ADD_U64(ctx, child, OID_AUTO, "rx_ebytes",
1314bd368728SMarko Zec 	    CTLFLAG_RD, &adapter->bytes_err, 0, "rx error bytes");
1315bd368728SMarko Zec 
1316bd368728SMarko Zec 	for (i = SUME_NPORTS - 1; i >= 0; i--) {
1317cc970676SJustin Hibbits 		if_t ifp = adapter->ifp[i];
1318bd368728SMarko Zec 		if (ifp == NULL)
1319bd368728SMarko Zec 			continue;
1320bd368728SMarko Zec 
1321cc970676SJustin Hibbits 		struct nf_priv *nf_priv = if_getsoftc(ifp);
1322bd368728SMarko Zec 
1323bd368728SMarko Zec 		snprintf(namebuf, MAX_IFC_NAME_LEN, "%s%d",
1324bd368728SMarko Zec 		    SUME_ETH_DEVICE_NAME, nf_priv->unit);
1325bd368728SMarko Zec 		tmp_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf,
1326bd368728SMarko Zec 		    CTLFLAG_RW, 0, "SUME ifc tree");
1327bd368728SMarko Zec 		if (tmp_tree == NULL) {
1328bd368728SMarko Zec 			device_printf(dev, "SYSCTL_ADD_NODE failed.\n");
1329bd368728SMarko Zec 			return;
1330bd368728SMarko Zec 		}
1331bd368728SMarko Zec 
1332bd368728SMarko Zec 		/* Packets dropped by down interface. */
1333bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1334bd368728SMarko Zec 		    "ifc_down_bytes", CTLFLAG_RD,
1335bd368728SMarko Zec 		    &nf_priv->stats.ifc_down_bytes, 0, "ifc_down bytes");
1336bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1337bd368728SMarko Zec 		    "ifc_down_packets", CTLFLAG_RD,
1338bd368728SMarko Zec 		    &nf_priv->stats.ifc_down_packets, 0, "ifc_down packets");
1339bd368728SMarko Zec 
1340bd368728SMarko Zec 		/* HW RX stats */
1341bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1342bd368728SMarko Zec 		    "hw_rx_packets", CTLFLAG_RD, &nf_priv->stats.hw_rx_packets,
1343bd368728SMarko Zec 		    0, "hw_rx packets");
1344bd368728SMarko Zec 
1345bd368728SMarko Zec 		/* HW TX stats */
1346bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1347bd368728SMarko Zec 		    "hw_tx_packets", CTLFLAG_RD, &nf_priv->stats.hw_tx_packets,
1348bd368728SMarko Zec 		    0, "hw_tx packets");
1349bd368728SMarko Zec 
1350bd368728SMarko Zec 		/* RX stats */
1351bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1352bd368728SMarko Zec 		    "rx_bytes", CTLFLAG_RD, &nf_priv->stats.rx_bytes, 0,
1353bd368728SMarko Zec 		    "rx bytes");
1354bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1355bd368728SMarko Zec 		    "rx_dropped", CTLFLAG_RD, &nf_priv->stats.rx_dropped, 0,
1356bd368728SMarko Zec 		    "rx dropped");
1357bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1358bd368728SMarko Zec 		    "rx_packets", CTLFLAG_RD, &nf_priv->stats.rx_packets, 0,
1359bd368728SMarko Zec 		    "rx packets");
1360bd368728SMarko Zec 
1361bd368728SMarko Zec 		/* TX stats */
1362bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1363bd368728SMarko Zec 		    "tx_bytes", CTLFLAG_RD, &nf_priv->stats.tx_bytes, 0,
1364bd368728SMarko Zec 		    "tx bytes");
1365bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1366bd368728SMarko Zec 		    "tx_dropped", CTLFLAG_RD, &nf_priv->stats.tx_dropped, 0,
1367bd368728SMarko Zec 		    "tx dropped");
1368bd368728SMarko Zec 		SYSCTL_ADD_U64(ctx, SYSCTL_CHILDREN(tmp_tree), OID_AUTO,
1369bd368728SMarko Zec 		    "tx_packets", CTLFLAG_RD, &nf_priv->stats.tx_packets, 0,
1370bd368728SMarko Zec 		    "tx packets");
1371bd368728SMarko Zec 	}
1372bd368728SMarko Zec }
1373bd368728SMarko Zec 
1374bd368728SMarko Zec static void
sume_local_timer(void * arg)1375bd368728SMarko Zec sume_local_timer(void *arg)
1376bd368728SMarko Zec {
1377bd368728SMarko Zec 	struct sume_adapter *adapter = arg;
1378bd368728SMarko Zec 
1379bd368728SMarko Zec 	if (!adapter->running)
1380bd368728SMarko Zec 		return;
1381bd368728SMarko Zec 
1382bd368728SMarko Zec 	taskqueue_enqueue(adapter->tq, &adapter->stat_task);
1383bd368728SMarko Zec 
1384bd368728SMarko Zec 	SUME_LOCK(adapter);
1385bd368728SMarko Zec 	if (adapter->send[SUME_RIFFA_CHANNEL_DATA]->state !=
1386bd368728SMarko Zec 	    SUME_RIFFA_CHAN_STATE_IDLE && ++adapter->wd_counter >= 3) {
1387bd368728SMarko Zec 		/* Resetting interfaces if stuck for 3 seconds. */
1388bd368728SMarko Zec 		device_printf(adapter->dev, "TX stuck, resetting adapter.\n");
1389bd368728SMarko Zec 		read_reg(adapter, RIFFA_INFO_REG_OFF);
1390bd368728SMarko Zec 
1391bd368728SMarko Zec 		adapter->send[SUME_RIFFA_CHANNEL_DATA]->state =
1392bd368728SMarko Zec 		    SUME_RIFFA_CHAN_STATE_IDLE;
1393bd368728SMarko Zec 		adapter->wd_counter = 0;
1394bd368728SMarko Zec 
1395bd368728SMarko Zec 		check_tx_queues(adapter);
1396bd368728SMarko Zec 	}
1397bd368728SMarko Zec 	SUME_UNLOCK(adapter);
1398bd368728SMarko Zec 
1399bd368728SMarko Zec 	callout_reset(&adapter->timer, 1 * hz, sume_local_timer, adapter);
1400bd368728SMarko Zec }
1401bd368728SMarko Zec 
1402bd368728SMarko Zec static void
sume_get_stats(void * context,int pending)1403bd368728SMarko Zec sume_get_stats(void *context, int pending)
1404bd368728SMarko Zec {
1405bd368728SMarko Zec 	struct sume_adapter *adapter = context;
1406bd368728SMarko Zec 	int i;
1407bd368728SMarko Zec 
1408bd368728SMarko Zec 	for (i = 0; i < SUME_NPORTS; i++) {
1409cc970676SJustin Hibbits 		if_t ifp = adapter->ifp[i];
1410bd368728SMarko Zec 
1411cc970676SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
1412cc970676SJustin Hibbits 			struct nf_priv *nf_priv = if_getsoftc(ifp);
1413bd368728SMarko Zec 			struct sume_ifreq sifr;
1414bd368728SMarko Zec 
1415bd368728SMarko Zec 			sume_update_link_status(ifp);
1416bd368728SMarko Zec 
1417bd368728SMarko Zec 			/* Get RX counter. */
1418bd368728SMarko Zec 			sifr.addr = SUME_STAT_RX_ADDR(nf_priv->port);
1419bd368728SMarko Zec 			sifr.val = 0;
1420bd368728SMarko Zec 
1421bd368728SMarko Zec 			if (!get_modreg_value(nf_priv, &sifr))
1422bd368728SMarko Zec 				nf_priv->stats.hw_rx_packets += sifr.val;
1423bd368728SMarko Zec 
1424bd368728SMarko Zec 			/* Get TX counter. */
1425bd368728SMarko Zec 			sifr.addr = SUME_STAT_TX_ADDR(nf_priv->port);
1426bd368728SMarko Zec 			sifr.val = 0;
1427bd368728SMarko Zec 
1428bd368728SMarko Zec 			if (!get_modreg_value(nf_priv, &sifr))
1429bd368728SMarko Zec 				nf_priv->stats.hw_tx_packets += sifr.val;
1430bd368728SMarko Zec 		}
1431bd368728SMarko Zec 	}
1432bd368728SMarko Zec }
1433bd368728SMarko Zec 
1434bd368728SMarko Zec static int
sume_attach(device_t dev)1435bd368728SMarko Zec sume_attach(device_t dev)
1436bd368728SMarko Zec {
1437bd368728SMarko Zec 	struct sume_adapter *adapter = device_get_softc(dev);
1438bd368728SMarko Zec 	adapter->dev = dev;
1439bd368728SMarko Zec 	int error, i;
1440bd368728SMarko Zec 
1441bd368728SMarko Zec 	mtx_init(&adapter->lock, "Global lock", NULL, MTX_DEF);
1442bd368728SMarko Zec 
1443bd368728SMarko Zec 	adapter->running = 0;
1444bd368728SMarko Zec 
1445bd368728SMarko Zec 	/* OK finish up RIFFA. */
1446bd368728SMarko Zec 	error = sume_probe_riffa_pci(adapter);
1447bd368728SMarko Zec 	if (error != 0)
1448bd368728SMarko Zec 		goto error;
1449bd368728SMarko Zec 
1450bd368728SMarko Zec 	error = sume_probe_riffa_buffers(adapter);
1451bd368728SMarko Zec 	if (error != 0)
1452bd368728SMarko Zec 		goto error;
1453bd368728SMarko Zec 
1454bd368728SMarko Zec 	/* Now do the network interfaces. */
1455bd368728SMarko Zec 	for (i = 0; i < SUME_NPORTS; i++) {
1456bd368728SMarko Zec 		error = sume_ifp_alloc(adapter, i);
1457bd368728SMarko Zec 		if (error != 0)
1458bd368728SMarko Zec 			goto error;
1459bd368728SMarko Zec 	}
1460bd368728SMarko Zec 
1461bd368728SMarko Zec 	/*  Register stats and register sysctls. */
1462bd368728SMarko Zec 	sume_sysctl_init(adapter);
1463bd368728SMarko Zec 
1464bd368728SMarko Zec 	/* Reset the HW. */
1465bd368728SMarko Zec 	read_reg(adapter, RIFFA_INFO_REG_OFF);
1466bd368728SMarko Zec 
1467bd368728SMarko Zec 	/* Ready to go, "enable" IRQ. */
1468bd368728SMarko Zec 	adapter->running = 1;
1469bd368728SMarko Zec 
1470bd368728SMarko Zec 	callout_init(&adapter->timer, 1);
1471bd368728SMarko Zec 	TASK_INIT(&adapter->stat_task, 0, sume_get_stats, adapter);
1472bd368728SMarko Zec 
1473bd368728SMarko Zec 	adapter->tq = taskqueue_create("sume_stats", M_NOWAIT,
1474bd368728SMarko Zec 	    taskqueue_thread_enqueue, &adapter->tq);
1475bd368728SMarko Zec 	taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s stattaskq",
1476bd368728SMarko Zec 	    device_get_nameunit(adapter->dev));
1477bd368728SMarko Zec 
1478bd368728SMarko Zec 	callout_reset(&adapter->timer, 1 * hz, sume_local_timer, adapter);
1479bd368728SMarko Zec 
1480bd368728SMarko Zec 	return (0);
1481bd368728SMarko Zec 
1482bd368728SMarko Zec error:
1483bd368728SMarko Zec 	sume_detach(dev);
1484bd368728SMarko Zec 
1485bd368728SMarko Zec 	return (error);
1486bd368728SMarko Zec }
1487bd368728SMarko Zec 
1488bd368728SMarko Zec static void
sume_remove_riffa_buffer(const struct sume_adapter * adapter,struct riffa_chnl_dir ** pp)1489bd368728SMarko Zec sume_remove_riffa_buffer(const struct sume_adapter *adapter,
1490bd368728SMarko Zec     struct riffa_chnl_dir **pp)
1491bd368728SMarko Zec {
1492bd368728SMarko Zec 	int ch;
1493bd368728SMarko Zec 
1494bd368728SMarko Zec 	for (ch = 0; ch < SUME_RIFFA_CHANNELS; ch++) {
1495bd368728SMarko Zec 		if (pp[ch] == NULL)
1496bd368728SMarko Zec 			continue;
1497bd368728SMarko Zec 
1498bd368728SMarko Zec 		if (pp[ch]->buf_hw_addr != 0) {
1499bd368728SMarko Zec 			bus_dmamem_free(pp[ch]->ch_tag, pp[ch]->buf_addr,
1500bd368728SMarko Zec 			    pp[ch]->ch_map);
1501bd368728SMarko Zec 			pp[ch]->buf_hw_addr = 0;
1502bd368728SMarko Zec 		}
1503bd368728SMarko Zec 
1504bd368728SMarko Zec 		free(pp[ch], M_SUME);
1505bd368728SMarko Zec 	}
1506bd368728SMarko Zec }
1507bd368728SMarko Zec 
1508bd368728SMarko Zec static void
sume_remove_riffa_buffers(struct sume_adapter * adapter)1509bd368728SMarko Zec sume_remove_riffa_buffers(struct sume_adapter *adapter)
1510bd368728SMarko Zec {
1511bd368728SMarko Zec 	if (adapter->send != NULL) {
1512bd368728SMarko Zec 		sume_remove_riffa_buffer(adapter, adapter->send);
1513bd368728SMarko Zec 		free(adapter->send, M_SUME);
1514bd368728SMarko Zec 		adapter->send = NULL;
1515bd368728SMarko Zec 	}
1516bd368728SMarko Zec 	if (adapter->recv != NULL) {
1517bd368728SMarko Zec 		sume_remove_riffa_buffer(adapter, adapter->recv);
1518bd368728SMarko Zec 		free(adapter->recv, M_SUME);
1519bd368728SMarko Zec 		adapter->recv = NULL;
1520bd368728SMarko Zec 	}
1521bd368728SMarko Zec }
1522bd368728SMarko Zec 
1523bd368728SMarko Zec static int
sume_detach(device_t dev)1524bd368728SMarko Zec sume_detach(device_t dev)
1525bd368728SMarko Zec {
1526bd368728SMarko Zec 	struct sume_adapter *adapter = device_get_softc(dev);
1527bd368728SMarko Zec 	int i;
1528bd368728SMarko Zec 	struct nf_priv *nf_priv;
1529bd368728SMarko Zec 
1530bd368728SMarko Zec 	KASSERT(mtx_initialized(&adapter->lock), ("SUME mutex not "
1531bd368728SMarko Zec 	    "initialized"));
1532bd368728SMarko Zec 	adapter->running = 0;
1533bd368728SMarko Zec 
1534bd368728SMarko Zec 	/* Drain the stats callout and task queue. */
1535bd368728SMarko Zec 	callout_drain(&adapter->timer);
1536bd368728SMarko Zec 
1537bd368728SMarko Zec 	if (adapter->tq) {
1538bd368728SMarko Zec 		taskqueue_drain(adapter->tq, &adapter->stat_task);
1539bd368728SMarko Zec 		taskqueue_free(adapter->tq);
1540bd368728SMarko Zec 	}
1541bd368728SMarko Zec 
1542bd368728SMarko Zec 	for (i = 0; i < SUME_NPORTS; i++) {
1543cc970676SJustin Hibbits 		if_t ifp = adapter->ifp[i];
1544bd368728SMarko Zec 		if (ifp == NULL)
1545bd368728SMarko Zec 			continue;
1546bd368728SMarko Zec 
1547cc970676SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
1548cc970676SJustin Hibbits 		nf_priv = if_getsoftc(ifp);
1549bd368728SMarko Zec 
1550cc970676SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP)
1551bd368728SMarko Zec 			if_down(ifp);
1552bd368728SMarko Zec 		ifmedia_removeall(&nf_priv->media);
1553bd368728SMarko Zec 		free_unr(unr, nf_priv->unit);
1554bd368728SMarko Zec 
1555cc970676SJustin Hibbits 		if_setflagbits(ifp, 0, IFF_UP);
1556bd368728SMarko Zec 		ether_ifdetach(ifp);
1557bd368728SMarko Zec 		if_free(ifp);
1558bd368728SMarko Zec 
1559bd368728SMarko Zec 		free(nf_priv, M_SUME);
1560bd368728SMarko Zec 	}
1561bd368728SMarko Zec 
1562bd368728SMarko Zec 	sume_remove_riffa_buffers(adapter);
1563bd368728SMarko Zec 
1564bd368728SMarko Zec 	if (adapter->irq.tag)
1565bd368728SMarko Zec 		bus_teardown_intr(dev, adapter->irq.res, adapter->irq.tag);
1566bd368728SMarko Zec 	if (adapter->irq.res)
1567bd368728SMarko Zec 		bus_release_resource(dev, SYS_RES_IRQ, adapter->irq.rid,
1568bd368728SMarko Zec 		    adapter->irq.res);
1569bd368728SMarko Zec 
1570bd368728SMarko Zec 	pci_release_msi(dev);
1571bd368728SMarko Zec 
1572bd368728SMarko Zec 	if (adapter->bar0_addr)
1573bd368728SMarko Zec 		bus_release_resource(dev, SYS_RES_MEMORY, adapter->rid,
1574bd368728SMarko Zec 		    adapter->bar0_addr);
1575bd368728SMarko Zec 
1576bd368728SMarko Zec 	mtx_destroy(&adapter->lock);
1577bd368728SMarko Zec 
1578bd368728SMarko Zec 	return (0);
1579bd368728SMarko Zec }
1580bd368728SMarko Zec 
1581bd368728SMarko Zec static int
mod_event(module_t mod,int cmd,void * arg)1582bd368728SMarko Zec mod_event(module_t mod, int cmd, void *arg)
1583bd368728SMarko Zec {
1584bd368728SMarko Zec 	switch (cmd) {
1585bd368728SMarko Zec 	case MOD_LOAD:
1586bd368728SMarko Zec 		unr = new_unrhdr(0, INT_MAX, NULL);
1587bd368728SMarko Zec 		break;
1588bd368728SMarko Zec 
1589bd368728SMarko Zec 	case MOD_UNLOAD:
1590bd368728SMarko Zec 		delete_unrhdr(unr);
1591bd368728SMarko Zec 		break;
1592bd368728SMarko Zec 	}
1593bd368728SMarko Zec 
1594bd368728SMarko Zec 	return (0);
1595bd368728SMarko Zec }
1596bd368728SMarko Zec 
159710155347SJohn Baldwin DRIVER_MODULE(sume, pci, sume_driver, mod_event, NULL);
1598bd368728SMarko Zec MODULE_VERSION(sume, 1);
1599