xref: /freebsd/sys/dev/qlxge/qls_os.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
1718cf2ccSPedro F. Giffuni /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4711bcba0SDavid C Somayajulu  * Copyright (c) 2013-2014 Qlogic Corporation
5711bcba0SDavid C Somayajulu  * All rights reserved.
6711bcba0SDavid C Somayajulu  *
7711bcba0SDavid C Somayajulu  *  Redistribution and use in source and binary forms, with or without
8711bcba0SDavid C Somayajulu  *  modification, are permitted provided that the following conditions
9711bcba0SDavid C Somayajulu  *  are met:
10711bcba0SDavid C Somayajulu  *
11711bcba0SDavid C Somayajulu  *  1. Redistributions of source code must retain the above copyright
12711bcba0SDavid C Somayajulu  *     notice, this list of conditions and the following disclaimer.
13711bcba0SDavid C Somayajulu  *  2. Redistributions in binary form must reproduce the above copyright
14711bcba0SDavid C Somayajulu  *     notice, this list of conditions and the following disclaimer in the
15711bcba0SDavid C Somayajulu  *     documentation and/or other materials provided with the distribution.
16711bcba0SDavid C Somayajulu  *
17711bcba0SDavid C Somayajulu  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18711bcba0SDavid C Somayajulu  *  and ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19711bcba0SDavid C Somayajulu  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20711bcba0SDavid C Somayajulu  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21711bcba0SDavid C Somayajulu  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22711bcba0SDavid C Somayajulu  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23711bcba0SDavid C Somayajulu  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24711bcba0SDavid C Somayajulu  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25711bcba0SDavid C Somayajulu  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26711bcba0SDavid C Somayajulu  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27711bcba0SDavid C Somayajulu  *  POSSIBILITY OF SUCH DAMAGE.
28711bcba0SDavid C Somayajulu  */
29711bcba0SDavid C Somayajulu 
30711bcba0SDavid C Somayajulu /*
31711bcba0SDavid C Somayajulu  * File: qls_os.c
32711bcba0SDavid C Somayajulu  * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656.
33711bcba0SDavid C Somayajulu  */
34711bcba0SDavid C Somayajulu 
35711bcba0SDavid C Somayajulu #include <sys/cdefs.h>
36711bcba0SDavid C Somayajulu __FBSDID("$FreeBSD$");
37711bcba0SDavid C Somayajulu 
38711bcba0SDavid C Somayajulu 
39711bcba0SDavid C Somayajulu #include "qls_os.h"
40711bcba0SDavid C Somayajulu #include "qls_hw.h"
41711bcba0SDavid C Somayajulu #include "qls_def.h"
42711bcba0SDavid C Somayajulu #include "qls_inline.h"
43711bcba0SDavid C Somayajulu #include "qls_ver.h"
44711bcba0SDavid C Somayajulu #include "qls_glbl.h"
45711bcba0SDavid C Somayajulu #include "qls_dbg.h"
46711bcba0SDavid C Somayajulu #include <sys/smp.h>
47711bcba0SDavid C Somayajulu 
48711bcba0SDavid C Somayajulu /*
49711bcba0SDavid C Somayajulu  * Some PCI Configuration Space Related Defines
50711bcba0SDavid C Somayajulu  */
51711bcba0SDavid C Somayajulu 
52711bcba0SDavid C Somayajulu #ifndef PCI_VENDOR_QLOGIC
53711bcba0SDavid C Somayajulu #define PCI_VENDOR_QLOGIC	0x1077
54711bcba0SDavid C Somayajulu #endif
55711bcba0SDavid C Somayajulu 
56711bcba0SDavid C Somayajulu #ifndef PCI_DEVICE_QLOGIC_8000
57711bcba0SDavid C Somayajulu #define PCI_DEVICE_QLOGIC_8000	0x8000
58711bcba0SDavid C Somayajulu #endif
59711bcba0SDavid C Somayajulu 
60711bcba0SDavid C Somayajulu #define PCI_QLOGIC_DEV8000 \
61711bcba0SDavid C Somayajulu 	((PCI_DEVICE_QLOGIC_8000 << 16) | PCI_VENDOR_QLOGIC)
62711bcba0SDavid C Somayajulu 
63711bcba0SDavid C Somayajulu /*
64711bcba0SDavid C Somayajulu  * static functions
65711bcba0SDavid C Somayajulu  */
66711bcba0SDavid C Somayajulu static int qls_alloc_parent_dma_tag(qla_host_t *ha);
67711bcba0SDavid C Somayajulu static void qls_free_parent_dma_tag(qla_host_t *ha);
68711bcba0SDavid C Somayajulu 
69711bcba0SDavid C Somayajulu static void qls_flush_xmt_bufs(qla_host_t *ha);
70711bcba0SDavid C Somayajulu 
71711bcba0SDavid C Somayajulu static int qls_alloc_rcv_bufs(qla_host_t *ha);
72711bcba0SDavid C Somayajulu static void qls_free_rcv_bufs(qla_host_t *ha);
73711bcba0SDavid C Somayajulu 
74711bcba0SDavid C Somayajulu static void qls_init_ifnet(device_t dev, qla_host_t *ha);
75711bcba0SDavid C Somayajulu static void qls_release(qla_host_t *ha);
76711bcba0SDavid C Somayajulu static void qls_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
77711bcba0SDavid C Somayajulu 		int error);
78711bcba0SDavid C Somayajulu static void qls_stop(qla_host_t *ha);
79711bcba0SDavid C Somayajulu static int qls_send(qla_host_t *ha, struct mbuf **m_headp);
80711bcba0SDavid C Somayajulu static void qls_tx_done(void *context, int pending);
81711bcba0SDavid C Somayajulu 
82711bcba0SDavid C Somayajulu static int qls_config_lro(qla_host_t *ha);
83711bcba0SDavid C Somayajulu static void qls_free_lro(qla_host_t *ha);
84711bcba0SDavid C Somayajulu 
85711bcba0SDavid C Somayajulu static void qls_error_recovery(void *context, int pending);
86711bcba0SDavid C Somayajulu 
87711bcba0SDavid C Somayajulu /*
88711bcba0SDavid C Somayajulu  * Hooks to the Operating Systems
89711bcba0SDavid C Somayajulu  */
90711bcba0SDavid C Somayajulu static int qls_pci_probe (device_t);
91711bcba0SDavid C Somayajulu static int qls_pci_attach (device_t);
92711bcba0SDavid C Somayajulu static int qls_pci_detach (device_t);
93711bcba0SDavid C Somayajulu 
94711bcba0SDavid C Somayajulu static void qls_start(struct ifnet *ifp);
95711bcba0SDavid C Somayajulu static void qls_init(void *arg);
96711bcba0SDavid C Somayajulu static int qls_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
97711bcba0SDavid C Somayajulu static int qls_media_change(struct ifnet *ifp);
98711bcba0SDavid C Somayajulu static void qls_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
99711bcba0SDavid C Somayajulu 
100711bcba0SDavid C Somayajulu static device_method_t qla_pci_methods[] = {
101711bcba0SDavid C Somayajulu 	/* Device interface */
102711bcba0SDavid C Somayajulu 	DEVMETHOD(device_probe, qls_pci_probe),
103711bcba0SDavid C Somayajulu 	DEVMETHOD(device_attach, qls_pci_attach),
104711bcba0SDavid C Somayajulu 	DEVMETHOD(device_detach, qls_pci_detach),
105711bcba0SDavid C Somayajulu 	{ 0, 0 }
106711bcba0SDavid C Somayajulu };
107711bcba0SDavid C Somayajulu 
108711bcba0SDavid C Somayajulu static driver_t qla_pci_driver = {
109711bcba0SDavid C Somayajulu 	"ql", qla_pci_methods, sizeof (qla_host_t),
110711bcba0SDavid C Somayajulu };
111711bcba0SDavid C Somayajulu 
112711bcba0SDavid C Somayajulu static devclass_t qla8000_devclass;
113711bcba0SDavid C Somayajulu 
114711bcba0SDavid C Somayajulu DRIVER_MODULE(qla8000, pci, qla_pci_driver, qla8000_devclass, 0, 0);
115711bcba0SDavid C Somayajulu 
116711bcba0SDavid C Somayajulu MODULE_DEPEND(qla8000, pci, 1, 1, 1);
117711bcba0SDavid C Somayajulu MODULE_DEPEND(qla8000, ether, 1, 1, 1);
118711bcba0SDavid C Somayajulu 
119711bcba0SDavid C Somayajulu MALLOC_DEFINE(M_QLA8000BUF, "qla8000buf", "Buffers for qla8000 driver");
120711bcba0SDavid C Somayajulu 
121711bcba0SDavid C Somayajulu static char dev_str[64];
122711bcba0SDavid C Somayajulu static char ver_str[64];
123711bcba0SDavid C Somayajulu 
124711bcba0SDavid C Somayajulu /*
125711bcba0SDavid C Somayajulu  * Name:	qls_pci_probe
126711bcba0SDavid C Somayajulu  * Function:	Validate the PCI device to be a QLA80XX device
127711bcba0SDavid C Somayajulu  */
128711bcba0SDavid C Somayajulu static int
129711bcba0SDavid C Somayajulu qls_pci_probe(device_t dev)
130711bcba0SDavid C Somayajulu {
131711bcba0SDavid C Somayajulu         switch ((pci_get_device(dev) << 16) | (pci_get_vendor(dev))) {
132711bcba0SDavid C Somayajulu         case PCI_QLOGIC_DEV8000:
133711bcba0SDavid C Somayajulu 		snprintf(dev_str, sizeof(dev_str), "%s v%d.%d.%d",
134711bcba0SDavid C Somayajulu 			"Qlogic ISP 8000 PCI CNA Adapter-Ethernet Function",
135711bcba0SDavid C Somayajulu 			QLA_VERSION_MAJOR, QLA_VERSION_MINOR,
136711bcba0SDavid C Somayajulu 			QLA_VERSION_BUILD);
137711bcba0SDavid C Somayajulu 		snprintf(ver_str, sizeof(ver_str), "v%d.%d.%d",
138711bcba0SDavid C Somayajulu 			QLA_VERSION_MAJOR, QLA_VERSION_MINOR,
139711bcba0SDavid C Somayajulu 			QLA_VERSION_BUILD);
140711bcba0SDavid C Somayajulu                 device_set_desc(dev, dev_str);
141711bcba0SDavid C Somayajulu                 break;
142711bcba0SDavid C Somayajulu         default:
143711bcba0SDavid C Somayajulu                 return (ENXIO);
144711bcba0SDavid C Somayajulu         }
145711bcba0SDavid C Somayajulu 
146711bcba0SDavid C Somayajulu         if (bootverbose)
147711bcba0SDavid C Somayajulu                 printf("%s: %s\n ", __func__, dev_str);
148711bcba0SDavid C Somayajulu 
149711bcba0SDavid C Somayajulu         return (BUS_PROBE_DEFAULT);
150711bcba0SDavid C Somayajulu }
151711bcba0SDavid C Somayajulu 
152711bcba0SDavid C Somayajulu static int
153711bcba0SDavid C Somayajulu qls_sysctl_get_drvr_stats(SYSCTL_HANDLER_ARGS)
154711bcba0SDavid C Somayajulu {
155711bcba0SDavid C Somayajulu         int err = 0, ret;
156711bcba0SDavid C Somayajulu         qla_host_t *ha;
157711bcba0SDavid C Somayajulu         uint32_t i;
158711bcba0SDavid C Somayajulu 
159711bcba0SDavid C Somayajulu         err = sysctl_handle_int(oidp, &ret, 0, req);
160711bcba0SDavid C Somayajulu 
161711bcba0SDavid C Somayajulu         if (err || !req->newptr)
162711bcba0SDavid C Somayajulu                 return (err);
163711bcba0SDavid C Somayajulu 
164711bcba0SDavid C Somayajulu         if (ret == 1) {
165711bcba0SDavid C Somayajulu 
166711bcba0SDavid C Somayajulu                 ha = (qla_host_t *)arg1;
167711bcba0SDavid C Somayajulu 
168711bcba0SDavid C Somayajulu                 for (i = 0; i < ha->num_tx_rings; i++) {
169711bcba0SDavid C Somayajulu 
170711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
171711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].tx_frames= %p\n",
172711bcba0SDavid C Somayajulu 				__func__, i,
173711bcba0SDavid C Somayajulu                                 (void *)ha->tx_ring[i].tx_frames);
174711bcba0SDavid C Somayajulu 
175711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
176711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].tx_tso_frames= %p\n",
177711bcba0SDavid C Somayajulu 				__func__, i,
178711bcba0SDavid C Somayajulu                                 (void *)ha->tx_ring[i].tx_tso_frames);
179711bcba0SDavid C Somayajulu 
180711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
181711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].tx_vlan_frames= %p\n",
182711bcba0SDavid C Somayajulu 				__func__, i,
183711bcba0SDavid C Somayajulu                                 (void *)ha->tx_ring[i].tx_vlan_frames);
184711bcba0SDavid C Somayajulu 
185711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
186711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].txr_free= 0x%08x\n",
187711bcba0SDavid C Somayajulu 				__func__, i,
188711bcba0SDavid C Somayajulu                                 ha->tx_ring[i].txr_free);
189711bcba0SDavid C Somayajulu 
190711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
191711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].txr_next= 0x%08x\n",
192711bcba0SDavid C Somayajulu 				__func__, i,
193711bcba0SDavid C Somayajulu                                 ha->tx_ring[i].txr_next);
194711bcba0SDavid C Somayajulu 
195711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
196711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].txr_done= 0x%08x\n",
197711bcba0SDavid C Somayajulu 				__func__, i,
198711bcba0SDavid C Somayajulu                                 ha->tx_ring[i].txr_done);
199711bcba0SDavid C Somayajulu 
200711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
201711bcba0SDavid C Somayajulu                                 "%s: tx_ring[%d].txr_cons_idx= 0x%08x\n",
202711bcba0SDavid C Somayajulu 				__func__, i,
203711bcba0SDavid C Somayajulu                                 *(ha->tx_ring[i].txr_cons_vaddr));
204711bcba0SDavid C Somayajulu 		}
205711bcba0SDavid C Somayajulu 
206711bcba0SDavid C Somayajulu                 for (i = 0; i < ha->num_rx_rings; i++) {
207711bcba0SDavid C Somayajulu 
208711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
209711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].rx_int= %p\n",
210711bcba0SDavid C Somayajulu 				__func__, i,
211711bcba0SDavid C Somayajulu                                 (void *)ha->rx_ring[i].rx_int);
212711bcba0SDavid C Somayajulu 
213711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
214711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].rss_int= %p\n",
215711bcba0SDavid C Somayajulu 				__func__, i,
216711bcba0SDavid C Somayajulu                                 (void *)ha->rx_ring[i].rss_int);
217711bcba0SDavid C Somayajulu 
218711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
219711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].lbq_next= 0x%08x\n",
220711bcba0SDavid C Somayajulu 				__func__, i,
221711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].lbq_next);
222711bcba0SDavid C Somayajulu 
223711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
224711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].lbq_free= 0x%08x\n",
225711bcba0SDavid C Somayajulu 				__func__, i,
226711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].lbq_free);
227711bcba0SDavid C Somayajulu 
228711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
229711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].lbq_in= 0x%08x\n",
230711bcba0SDavid C Somayajulu 				__func__, i,
231711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].lbq_in);
232711bcba0SDavid C Somayajulu 
233711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
234711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].sbq_next= 0x%08x\n",
235711bcba0SDavid C Somayajulu 				__func__, i,
236711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].sbq_next);
237711bcba0SDavid C Somayajulu 
238711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
239711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].sbq_free= 0x%08x\n",
240711bcba0SDavid C Somayajulu 				__func__, i,
241711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].sbq_free);
242711bcba0SDavid C Somayajulu 
243711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev,
244711bcba0SDavid C Somayajulu                                 "%s: rx_ring[%d].sbq_in= 0x%08x\n",
245711bcba0SDavid C Somayajulu 				__func__, i,
246711bcba0SDavid C Somayajulu                                 ha->rx_ring[i].sbq_in);
247711bcba0SDavid C Somayajulu 		}
248711bcba0SDavid C Somayajulu 
249711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev, "%s: err_m_getcl = 0x%08x\n",
250711bcba0SDavid C Somayajulu 				__func__, ha->err_m_getcl);
251711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev, "%s: err_m_getjcl = 0x%08x\n",
252711bcba0SDavid C Somayajulu 				__func__, ha->err_m_getjcl);
253711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev,
254711bcba0SDavid C Somayajulu 				"%s: err_tx_dmamap_create = 0x%08x\n",
255711bcba0SDavid C Somayajulu 				__func__, ha->err_tx_dmamap_create);
256711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev,
257711bcba0SDavid C Somayajulu 				"%s: err_tx_dmamap_load = 0x%08x\n",
258711bcba0SDavid C Somayajulu 				__func__, ha->err_tx_dmamap_load);
259711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev,
260711bcba0SDavid C Somayajulu 				"%s: err_tx_defrag = 0x%08x\n",
261711bcba0SDavid C Somayajulu 				__func__, ha->err_tx_defrag);
262711bcba0SDavid C Somayajulu         }
263711bcba0SDavid C Somayajulu         return (err);
264711bcba0SDavid C Somayajulu }
265711bcba0SDavid C Somayajulu 
266711bcba0SDavid C Somayajulu static void
267711bcba0SDavid C Somayajulu qls_add_sysctls(qla_host_t *ha)
268711bcba0SDavid C Somayajulu {
269711bcba0SDavid C Somayajulu         device_t dev = ha->pci_dev;
270711bcba0SDavid C Somayajulu 
271711bcba0SDavid C Somayajulu 	SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
272711bcba0SDavid C Somayajulu 		SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
273711bcba0SDavid C Somayajulu 		OID_AUTO, "version", CTLFLAG_RD,
274711bcba0SDavid C Somayajulu 		ver_str, 0, "Driver Version");
275711bcba0SDavid C Somayajulu 
276711bcba0SDavid C Somayajulu 	qls_dbg_level = 0;
277711bcba0SDavid C Somayajulu         SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
278711bcba0SDavid C Somayajulu                 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
279711bcba0SDavid C Somayajulu                 OID_AUTO, "debug", CTLFLAG_RW,
280711bcba0SDavid C Somayajulu                 &qls_dbg_level, qls_dbg_level, "Debug Level");
281711bcba0SDavid C Somayajulu 
282711bcba0SDavid C Somayajulu         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
283711bcba0SDavid C Somayajulu             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
284*7029da5cSPawel Biernacki             OID_AUTO, "drvr_stats",
285*7029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, (void *)ha, 0,
286711bcba0SDavid C Somayajulu 	    qls_sysctl_get_drvr_stats, "I", "Driver Maintained Statistics");
287711bcba0SDavid C Somayajulu 
288711bcba0SDavid C Somayajulu         return;
289711bcba0SDavid C Somayajulu }
290711bcba0SDavid C Somayajulu 
291711bcba0SDavid C Somayajulu static void
292711bcba0SDavid C Somayajulu qls_watchdog(void *arg)
293711bcba0SDavid C Somayajulu {
294711bcba0SDavid C Somayajulu 	qla_host_t *ha = arg;
295711bcba0SDavid C Somayajulu 	struct ifnet *ifp;
296711bcba0SDavid C Somayajulu 
297711bcba0SDavid C Somayajulu 	ifp = ha->ifp;
298711bcba0SDavid C Somayajulu 
299711bcba0SDavid C Somayajulu         if (ha->flags.qla_watchdog_exit) {
300711bcba0SDavid C Somayajulu 		ha->qla_watchdog_exited = 1;
301711bcba0SDavid C Somayajulu 		return;
302711bcba0SDavid C Somayajulu 	}
303711bcba0SDavid C Somayajulu 	ha->qla_watchdog_exited = 0;
304711bcba0SDavid C Somayajulu 
305711bcba0SDavid C Somayajulu 	if (!ha->flags.qla_watchdog_pause) {
306711bcba0SDavid C Somayajulu 
307711bcba0SDavid C Somayajulu 		if (ha->qla_initiate_recovery) {
308711bcba0SDavid C Somayajulu 
309711bcba0SDavid C Somayajulu 			ha->qla_watchdog_paused = 1;
310711bcba0SDavid C Somayajulu 			ha->qla_initiate_recovery = 0;
311711bcba0SDavid C Somayajulu 			ha->err_inject = 0;
312711bcba0SDavid C Somayajulu 			taskqueue_enqueue(ha->err_tq, &ha->err_task);
313711bcba0SDavid C Somayajulu 
314711bcba0SDavid C Somayajulu 		} else if ((ifp->if_snd.ifq_head != NULL) && QL_RUNNING(ifp)) {
315711bcba0SDavid C Somayajulu 
316711bcba0SDavid C Somayajulu 			taskqueue_enqueue(ha->tx_tq, &ha->tx_task);
317711bcba0SDavid C Somayajulu 		}
318711bcba0SDavid C Somayajulu 
319711bcba0SDavid C Somayajulu 		ha->qla_watchdog_paused = 0;
320711bcba0SDavid C Somayajulu 	} else {
321711bcba0SDavid C Somayajulu 		ha->qla_watchdog_paused = 1;
322711bcba0SDavid C Somayajulu 	}
323711bcba0SDavid C Somayajulu 
32482b5e644SConrad Meyer 	ha->watchdog_ticks = (ha->watchdog_ticks + 1) % 1000;
325711bcba0SDavid C Somayajulu 	callout_reset(&ha->tx_callout, QLA_WATCHDOG_CALLOUT_TICKS,
326711bcba0SDavid C Somayajulu 		qls_watchdog, ha);
327711bcba0SDavid C Somayajulu 
328711bcba0SDavid C Somayajulu 	return;
329711bcba0SDavid C Somayajulu }
330711bcba0SDavid C Somayajulu 
331711bcba0SDavid C Somayajulu /*
332711bcba0SDavid C Somayajulu  * Name:	qls_pci_attach
333711bcba0SDavid C Somayajulu  * Function:	attaches the device to the operating system
334711bcba0SDavid C Somayajulu  */
335711bcba0SDavid C Somayajulu static int
336711bcba0SDavid C Somayajulu qls_pci_attach(device_t dev)
337711bcba0SDavid C Somayajulu {
338711bcba0SDavid C Somayajulu 	qla_host_t *ha = NULL;
339711bcba0SDavid C Somayajulu 	int i;
340711bcba0SDavid C Somayajulu 
341711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: enter\n", __func__));
342711bcba0SDavid C Somayajulu 
343711bcba0SDavid C Somayajulu         if ((ha = device_get_softc(dev)) == NULL) {
344711bcba0SDavid C Somayajulu                 device_printf(dev, "cannot get softc\n");
345711bcba0SDavid C Somayajulu                 return (ENOMEM);
346711bcba0SDavid C Somayajulu         }
347711bcba0SDavid C Somayajulu 
348711bcba0SDavid C Somayajulu         memset(ha, 0, sizeof (qla_host_t));
349711bcba0SDavid C Somayajulu 
350711bcba0SDavid C Somayajulu         if (pci_get_device(dev) != PCI_DEVICE_QLOGIC_8000) {
351711bcba0SDavid C Somayajulu                 device_printf(dev, "device is not QLE8000\n");
352711bcba0SDavid C Somayajulu                 return (ENXIO);
353711bcba0SDavid C Somayajulu 	}
354711bcba0SDavid C Somayajulu 
355711bcba0SDavid C Somayajulu         ha->pci_func = pci_get_function(dev);
356711bcba0SDavid C Somayajulu 
357711bcba0SDavid C Somayajulu         ha->pci_dev = dev;
358711bcba0SDavid C Somayajulu 
359711bcba0SDavid C Somayajulu 	pci_enable_busmaster(dev);
360711bcba0SDavid C Somayajulu 
361711bcba0SDavid C Somayajulu 	ha->reg_rid = PCIR_BAR(1);
362711bcba0SDavid C Somayajulu 	ha->pci_reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ha->reg_rid,
363711bcba0SDavid C Somayajulu 				RF_ACTIVE);
364711bcba0SDavid C Somayajulu 
365711bcba0SDavid C Somayajulu         if (ha->pci_reg == NULL) {
366711bcba0SDavid C Somayajulu                 device_printf(dev, "unable to map any ports\n");
367711bcba0SDavid C Somayajulu                 goto qls_pci_attach_err;
368711bcba0SDavid C Somayajulu         }
369711bcba0SDavid C Somayajulu 
370711bcba0SDavid C Somayajulu 	ha->reg_rid1 = PCIR_BAR(3);
371711bcba0SDavid C Somayajulu 	ha->pci_reg1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
372711bcba0SDavid C Somayajulu 			&ha->reg_rid1, RF_ACTIVE);
373711bcba0SDavid C Somayajulu 
374711bcba0SDavid C Somayajulu         if (ha->pci_reg1 == NULL) {
375711bcba0SDavid C Somayajulu                 device_printf(dev, "unable to map any ports\n");
376711bcba0SDavid C Somayajulu                 goto qls_pci_attach_err;
377711bcba0SDavid C Somayajulu         }
378711bcba0SDavid C Somayajulu 
379711bcba0SDavid C Somayajulu 	mtx_init(&ha->hw_lock, "qla80xx_hw_lock", MTX_NETWORK_LOCK, MTX_DEF);
380711bcba0SDavid C Somayajulu 	mtx_init(&ha->tx_lock, "qla80xx_tx_lock", MTX_NETWORK_LOCK, MTX_DEF);
381711bcba0SDavid C Somayajulu 
382711bcba0SDavid C Somayajulu 	qls_add_sysctls(ha);
383711bcba0SDavid C Somayajulu 	qls_hw_add_sysctls(ha);
384711bcba0SDavid C Somayajulu 
385711bcba0SDavid C Somayajulu 	ha->flags.lock_init = 1;
386711bcba0SDavid C Somayajulu 
387711bcba0SDavid C Somayajulu 	ha->msix_count = pci_msix_count(dev);
388711bcba0SDavid C Somayajulu 
389711bcba0SDavid C Somayajulu 	if (ha->msix_count < qls_get_msix_count(ha)) {
390711bcba0SDavid C Somayajulu 		device_printf(dev, "%s: msix_count[%d] not enough\n", __func__,
391711bcba0SDavid C Somayajulu 			ha->msix_count);
392711bcba0SDavid C Somayajulu 		goto qls_pci_attach_err;
393711bcba0SDavid C Somayajulu 	}
394711bcba0SDavid C Somayajulu 
395711bcba0SDavid C Somayajulu 	ha->msix_count = qls_get_msix_count(ha);
396711bcba0SDavid C Somayajulu 
397711bcba0SDavid C Somayajulu 	device_printf(dev, "\n%s: ha %p pci_func 0x%x  msix_count 0x%x"
398711bcba0SDavid C Somayajulu 		" pci_reg %p pci_reg1 %p\n", __func__, ha,
399711bcba0SDavid C Somayajulu 		ha->pci_func, ha->msix_count, ha->pci_reg, ha->pci_reg1);
400711bcba0SDavid C Somayajulu 
401711bcba0SDavid C Somayajulu 	if (pci_alloc_msix(dev, &ha->msix_count)) {
402711bcba0SDavid C Somayajulu 		device_printf(dev, "%s: pci_alloc_msi[%d] failed\n", __func__,
403711bcba0SDavid C Somayajulu 			ha->msix_count);
404711bcba0SDavid C Somayajulu 		ha->msix_count = 0;
405711bcba0SDavid C Somayajulu 		goto qls_pci_attach_err;
406711bcba0SDavid C Somayajulu 	}
407711bcba0SDavid C Somayajulu 
408711bcba0SDavid C Somayajulu         for (i = 0; i < ha->num_rx_rings; i++) {
409711bcba0SDavid C Somayajulu                 ha->irq_vec[i].cq_idx = i;
410711bcba0SDavid C Somayajulu                 ha->irq_vec[i].ha = ha;
411711bcba0SDavid C Somayajulu                 ha->irq_vec[i].irq_rid = 1 + i;
412711bcba0SDavid C Somayajulu 
413711bcba0SDavid C Somayajulu                 ha->irq_vec[i].irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
414711bcba0SDavid C Somayajulu                                 &ha->irq_vec[i].irq_rid,
415711bcba0SDavid C Somayajulu                                 (RF_ACTIVE | RF_SHAREABLE));
416711bcba0SDavid C Somayajulu 
417711bcba0SDavid C Somayajulu                 if (ha->irq_vec[i].irq == NULL) {
418711bcba0SDavid C Somayajulu                         device_printf(dev, "could not allocate interrupt\n");
419711bcba0SDavid C Somayajulu                         goto qls_pci_attach_err;
420711bcba0SDavid C Somayajulu                 }
421711bcba0SDavid C Somayajulu 
422711bcba0SDavid C Somayajulu 		if (bus_setup_intr(dev, ha->irq_vec[i].irq,
423711bcba0SDavid C Somayajulu 			(INTR_TYPE_NET | INTR_MPSAFE), NULL, qls_isr,
424711bcba0SDavid C Somayajulu 			&ha->irq_vec[i], &ha->irq_vec[i].handle)) {
425711bcba0SDavid C Somayajulu 				device_printf(dev,
426711bcba0SDavid C Somayajulu 					"could not setup interrupt\n");
427711bcba0SDavid C Somayajulu 			goto qls_pci_attach_err;
428711bcba0SDavid C Somayajulu 		}
429711bcba0SDavid C Somayajulu         }
430711bcba0SDavid C Somayajulu 
431711bcba0SDavid C Somayajulu 	qls_rd_nic_params(ha);
432711bcba0SDavid C Somayajulu 
433711bcba0SDavid C Somayajulu 	/* allocate parent dma tag */
434711bcba0SDavid C Somayajulu 	if (qls_alloc_parent_dma_tag(ha)) {
435711bcba0SDavid C Somayajulu 		device_printf(dev, "%s: qls_alloc_parent_dma_tag failed\n",
436711bcba0SDavid C Somayajulu 			__func__);
437711bcba0SDavid C Somayajulu 		goto qls_pci_attach_err;
438711bcba0SDavid C Somayajulu 	}
439711bcba0SDavid C Somayajulu 
440711bcba0SDavid C Somayajulu 	/* alloc all dma buffers */
441711bcba0SDavid C Somayajulu 	if (qls_alloc_dma(ha)) {
442711bcba0SDavid C Somayajulu 		device_printf(dev, "%s: qls_alloc_dma failed\n", __func__);
443711bcba0SDavid C Somayajulu 		goto qls_pci_attach_err;
444711bcba0SDavid C Somayajulu 	}
445711bcba0SDavid C Somayajulu 
446711bcba0SDavid C Somayajulu 	/* create the o.s ethernet interface */
447711bcba0SDavid C Somayajulu 	qls_init_ifnet(dev, ha);
448711bcba0SDavid C Somayajulu 
449711bcba0SDavid C Somayajulu 	ha->flags.qla_watchdog_active = 1;
450711bcba0SDavid C Somayajulu 	ha->flags.qla_watchdog_pause = 1;
451711bcba0SDavid C Somayajulu 
452711bcba0SDavid C Somayajulu 	TASK_INIT(&ha->tx_task, 0, qls_tx_done, ha);
453711bcba0SDavid C Somayajulu 	ha->tx_tq = taskqueue_create_fast("qla_txq", M_NOWAIT,
454711bcba0SDavid C Somayajulu 			taskqueue_thread_enqueue, &ha->tx_tq);
455711bcba0SDavid C Somayajulu 	taskqueue_start_threads(&ha->tx_tq, 1, PI_NET, "%s txq",
456711bcba0SDavid C Somayajulu 		device_get_nameunit(ha->pci_dev));
457711bcba0SDavid C Somayajulu 
458fd90e2edSJung-uk Kim 	callout_init(&ha->tx_callout, 1);
459711bcba0SDavid C Somayajulu 	ha->flags.qla_callout_init = 1;
460711bcba0SDavid C Somayajulu 
461711bcba0SDavid C Somayajulu         /* create ioctl device interface */
462711bcba0SDavid C Somayajulu         if (qls_make_cdev(ha)) {
463711bcba0SDavid C Somayajulu                 device_printf(dev, "%s: qls_make_cdev failed\n", __func__);
464711bcba0SDavid C Somayajulu                 goto qls_pci_attach_err;
465711bcba0SDavid C Somayajulu         }
466711bcba0SDavid C Somayajulu 
467711bcba0SDavid C Somayajulu 	callout_reset(&ha->tx_callout, QLA_WATCHDOG_CALLOUT_TICKS,
468711bcba0SDavid C Somayajulu 		qls_watchdog, ha);
469711bcba0SDavid C Somayajulu 
470711bcba0SDavid C Somayajulu         TASK_INIT(&ha->err_task, 0, qls_error_recovery, ha);
471711bcba0SDavid C Somayajulu         ha->err_tq = taskqueue_create_fast("qla_errq", M_NOWAIT,
472711bcba0SDavid C Somayajulu                         taskqueue_thread_enqueue, &ha->err_tq);
473711bcba0SDavid C Somayajulu         taskqueue_start_threads(&ha->err_tq, 1, PI_NET, "%s errq",
474711bcba0SDavid C Somayajulu                 device_get_nameunit(ha->pci_dev));
475711bcba0SDavid C Somayajulu 
476711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: exit 0\n", __func__));
477711bcba0SDavid C Somayajulu         return (0);
478711bcba0SDavid C Somayajulu 
479711bcba0SDavid C Somayajulu qls_pci_attach_err:
480711bcba0SDavid C Somayajulu 
481711bcba0SDavid C Somayajulu 	qls_release(ha);
482711bcba0SDavid C Somayajulu 
483711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: exit ENXIO\n", __func__));
484711bcba0SDavid C Somayajulu         return (ENXIO);
485711bcba0SDavid C Somayajulu }
486711bcba0SDavid C Somayajulu 
487711bcba0SDavid C Somayajulu /*
488711bcba0SDavid C Somayajulu  * Name:	qls_pci_detach
489711bcba0SDavid C Somayajulu  * Function:	Unhooks the device from the operating system
490711bcba0SDavid C Somayajulu  */
491711bcba0SDavid C Somayajulu static int
492711bcba0SDavid C Somayajulu qls_pci_detach(device_t dev)
493711bcba0SDavid C Somayajulu {
494711bcba0SDavid C Somayajulu 	qla_host_t *ha = NULL;
495711bcba0SDavid C Somayajulu 	struct ifnet *ifp;
496711bcba0SDavid C Somayajulu 
497711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: enter\n", __func__));
498711bcba0SDavid C Somayajulu 
499711bcba0SDavid C Somayajulu         if ((ha = device_get_softc(dev)) == NULL) {
500711bcba0SDavid C Somayajulu                 device_printf(dev, "cannot get softc\n");
501711bcba0SDavid C Somayajulu                 return (ENOMEM);
502711bcba0SDavid C Somayajulu         }
503711bcba0SDavid C Somayajulu 
504711bcba0SDavid C Somayajulu 	ifp = ha->ifp;
505711bcba0SDavid C Somayajulu 
506711bcba0SDavid C Somayajulu 	(void)QLA_LOCK(ha, __func__, 0);
507711bcba0SDavid C Somayajulu 	qls_stop(ha);
508711bcba0SDavid C Somayajulu 	QLA_UNLOCK(ha, __func__);
509711bcba0SDavid C Somayajulu 
510711bcba0SDavid C Somayajulu 	qls_release(ha);
511711bcba0SDavid C Somayajulu 
512711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: exit\n", __func__));
513711bcba0SDavid C Somayajulu 
514711bcba0SDavid C Somayajulu         return (0);
515711bcba0SDavid C Somayajulu }
516711bcba0SDavid C Somayajulu 
517711bcba0SDavid C Somayajulu /*
518711bcba0SDavid C Somayajulu  * Name:	qls_release
519711bcba0SDavid C Somayajulu  * Function:	Releases the resources allocated for the device
520711bcba0SDavid C Somayajulu  */
521711bcba0SDavid C Somayajulu static void
522711bcba0SDavid C Somayajulu qls_release(qla_host_t *ha)
523711bcba0SDavid C Somayajulu {
524711bcba0SDavid C Somayajulu 	device_t dev;
525711bcba0SDavid C Somayajulu 	int i;
526711bcba0SDavid C Somayajulu 
527711bcba0SDavid C Somayajulu 	dev = ha->pci_dev;
528711bcba0SDavid C Somayajulu 
529711bcba0SDavid C Somayajulu 	if (ha->err_tq) {
530711bcba0SDavid C Somayajulu 		taskqueue_drain(ha->err_tq, &ha->err_task);
531711bcba0SDavid C Somayajulu 		taskqueue_free(ha->err_tq);
532711bcba0SDavid C Somayajulu 	}
533711bcba0SDavid C Somayajulu 
534711bcba0SDavid C Somayajulu 	if (ha->tx_tq) {
535711bcba0SDavid C Somayajulu 		taskqueue_drain(ha->tx_tq, &ha->tx_task);
536711bcba0SDavid C Somayajulu 		taskqueue_free(ha->tx_tq);
537711bcba0SDavid C Somayajulu 	}
538711bcba0SDavid C Somayajulu 
539711bcba0SDavid C Somayajulu 	qls_del_cdev(ha);
540711bcba0SDavid C Somayajulu 
541711bcba0SDavid C Somayajulu 	if (ha->flags.qla_watchdog_active) {
542711bcba0SDavid C Somayajulu 		ha->flags.qla_watchdog_exit = 1;
543711bcba0SDavid C Somayajulu 
544711bcba0SDavid C Somayajulu 		while (ha->qla_watchdog_exited == 0)
545711bcba0SDavid C Somayajulu 			qls_mdelay(__func__, 1);
546711bcba0SDavid C Somayajulu 	}
547711bcba0SDavid C Somayajulu 
548711bcba0SDavid C Somayajulu 	if (ha->flags.qla_callout_init)
549711bcba0SDavid C Somayajulu 		callout_stop(&ha->tx_callout);
550711bcba0SDavid C Somayajulu 
551711bcba0SDavid C Somayajulu 	if (ha->ifp != NULL)
552711bcba0SDavid C Somayajulu 		ether_ifdetach(ha->ifp);
553711bcba0SDavid C Somayajulu 
554711bcba0SDavid C Somayajulu 	qls_free_dma(ha);
555711bcba0SDavid C Somayajulu 	qls_free_parent_dma_tag(ha);
556711bcba0SDavid C Somayajulu 
557711bcba0SDavid C Somayajulu         for (i = 0; i < ha->num_rx_rings; i++) {
558711bcba0SDavid C Somayajulu 
559711bcba0SDavid C Somayajulu                 if (ha->irq_vec[i].handle) {
560711bcba0SDavid C Somayajulu                         (void)bus_teardown_intr(dev, ha->irq_vec[i].irq,
561711bcba0SDavid C Somayajulu                                         ha->irq_vec[i].handle);
562711bcba0SDavid C Somayajulu                 }
563711bcba0SDavid C Somayajulu 
564711bcba0SDavid C Somayajulu                 if (ha->irq_vec[i].irq) {
565711bcba0SDavid C Somayajulu                         (void)bus_release_resource(dev, SYS_RES_IRQ,
566711bcba0SDavid C Somayajulu                                 ha->irq_vec[i].irq_rid,
567711bcba0SDavid C Somayajulu                                 ha->irq_vec[i].irq);
568711bcba0SDavid C Somayajulu                 }
569711bcba0SDavid C Somayajulu         }
570711bcba0SDavid C Somayajulu 
571711bcba0SDavid C Somayajulu 	if (ha->msix_count)
572711bcba0SDavid C Somayajulu 		pci_release_msi(dev);
573711bcba0SDavid C Somayajulu 
574711bcba0SDavid C Somayajulu 	if (ha->flags.lock_init) {
575711bcba0SDavid C Somayajulu 		mtx_destroy(&ha->tx_lock);
576711bcba0SDavid C Somayajulu 		mtx_destroy(&ha->hw_lock);
577711bcba0SDavid C Somayajulu 	}
578711bcba0SDavid C Somayajulu 
579711bcba0SDavid C Somayajulu         if (ha->pci_reg)
580711bcba0SDavid C Somayajulu                 (void) bus_release_resource(dev, SYS_RES_MEMORY, ha->reg_rid,
581711bcba0SDavid C Somayajulu 				ha->pci_reg);
582711bcba0SDavid C Somayajulu 
583711bcba0SDavid C Somayajulu         if (ha->pci_reg1)
584711bcba0SDavid C Somayajulu                 (void) bus_release_resource(dev, SYS_RES_MEMORY, ha->reg_rid1,
585711bcba0SDavid C Somayajulu 				ha->pci_reg1);
586711bcba0SDavid C Somayajulu }
587711bcba0SDavid C Somayajulu 
588711bcba0SDavid C Somayajulu /*
589711bcba0SDavid C Somayajulu  * DMA Related Functions
590711bcba0SDavid C Somayajulu  */
591711bcba0SDavid C Somayajulu 
592711bcba0SDavid C Somayajulu static void
593711bcba0SDavid C Somayajulu qls_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
594711bcba0SDavid C Somayajulu {
595711bcba0SDavid C Somayajulu         *((bus_addr_t *)arg) = 0;
596711bcba0SDavid C Somayajulu 
597711bcba0SDavid C Somayajulu         if (error) {
598711bcba0SDavid C Somayajulu                 printf("%s: bus_dmamap_load failed (%d)\n", __func__, error);
599711bcba0SDavid C Somayajulu                 return;
600711bcba0SDavid C Somayajulu 	}
601711bcba0SDavid C Somayajulu 
602711bcba0SDavid C Somayajulu         *((bus_addr_t *)arg) = segs[0].ds_addr;
603711bcba0SDavid C Somayajulu 
604711bcba0SDavid C Somayajulu 	return;
605711bcba0SDavid C Somayajulu }
606711bcba0SDavid C Somayajulu 
607711bcba0SDavid C Somayajulu int
608711bcba0SDavid C Somayajulu qls_alloc_dmabuf(qla_host_t *ha, qla_dma_t *dma_buf)
609711bcba0SDavid C Somayajulu {
610711bcba0SDavid C Somayajulu         int             ret = 0;
611711bcba0SDavid C Somayajulu         device_t        dev;
612711bcba0SDavid C Somayajulu         bus_addr_t      b_addr;
613711bcba0SDavid C Somayajulu 
614711bcba0SDavid C Somayajulu         dev = ha->pci_dev;
615711bcba0SDavid C Somayajulu 
616711bcba0SDavid C Somayajulu         QL_DPRINT2((dev, "%s: enter\n", __func__));
617711bcba0SDavid C Somayajulu 
618711bcba0SDavid C Somayajulu         ret = bus_dma_tag_create(
619711bcba0SDavid C Somayajulu                         ha->parent_tag,/* parent */
620711bcba0SDavid C Somayajulu                         dma_buf->alignment,
621711bcba0SDavid C Somayajulu                         ((bus_size_t)(1ULL << 32)),/* boundary */
622711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXADDR,      /* lowaddr */
623711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXADDR,      /* highaddr */
624711bcba0SDavid C Somayajulu                         NULL, NULL,             /* filter, filterarg */
625711bcba0SDavid C Somayajulu                         dma_buf->size,          /* maxsize */
626711bcba0SDavid C Somayajulu                         1,                      /* nsegments */
627711bcba0SDavid C Somayajulu                         dma_buf->size,          /* maxsegsize */
628711bcba0SDavid C Somayajulu                         0,                      /* flags */
629711bcba0SDavid C Somayajulu                         NULL, NULL,             /* lockfunc, lockarg */
630711bcba0SDavid C Somayajulu                         &dma_buf->dma_tag);
631711bcba0SDavid C Somayajulu 
632711bcba0SDavid C Somayajulu         if (ret) {
633711bcba0SDavid C Somayajulu                 device_printf(dev, "%s: could not create dma tag\n", __func__);
634711bcba0SDavid C Somayajulu                 goto qls_alloc_dmabuf_exit;
635711bcba0SDavid C Somayajulu         }
636711bcba0SDavid C Somayajulu         ret = bus_dmamem_alloc(dma_buf->dma_tag,
637711bcba0SDavid C Somayajulu                         (void **)&dma_buf->dma_b,
638711bcba0SDavid C Somayajulu                         (BUS_DMA_ZERO | BUS_DMA_COHERENT | BUS_DMA_NOWAIT),
639711bcba0SDavid C Somayajulu                         &dma_buf->dma_map);
640711bcba0SDavid C Somayajulu         if (ret) {
641711bcba0SDavid C Somayajulu                 bus_dma_tag_destroy(dma_buf->dma_tag);
642711bcba0SDavid C Somayajulu                 device_printf(dev, "%s: bus_dmamem_alloc failed\n", __func__);
643711bcba0SDavid C Somayajulu                 goto qls_alloc_dmabuf_exit;
644711bcba0SDavid C Somayajulu         }
645711bcba0SDavid C Somayajulu 
646711bcba0SDavid C Somayajulu         ret = bus_dmamap_load(dma_buf->dma_tag,
647711bcba0SDavid C Somayajulu                         dma_buf->dma_map,
648711bcba0SDavid C Somayajulu                         dma_buf->dma_b,
649711bcba0SDavid C Somayajulu                         dma_buf->size,
650711bcba0SDavid C Somayajulu                         qls_dmamap_callback,
651711bcba0SDavid C Somayajulu                         &b_addr, BUS_DMA_NOWAIT);
652711bcba0SDavid C Somayajulu 
653711bcba0SDavid C Somayajulu         if (ret || !b_addr) {
654711bcba0SDavid C Somayajulu                 bus_dma_tag_destroy(dma_buf->dma_tag);
655711bcba0SDavid C Somayajulu                 bus_dmamem_free(dma_buf->dma_tag, dma_buf->dma_b,
656711bcba0SDavid C Somayajulu                         dma_buf->dma_map);
657711bcba0SDavid C Somayajulu                 ret = -1;
658711bcba0SDavid C Somayajulu                 goto qls_alloc_dmabuf_exit;
659711bcba0SDavid C Somayajulu         }
660711bcba0SDavid C Somayajulu 
661711bcba0SDavid C Somayajulu         dma_buf->dma_addr = b_addr;
662711bcba0SDavid C Somayajulu 
663711bcba0SDavid C Somayajulu qls_alloc_dmabuf_exit:
664711bcba0SDavid C Somayajulu         QL_DPRINT2((dev, "%s: exit ret 0x%08x tag %p map %p b %p sz 0x%x\n",
665711bcba0SDavid C Somayajulu                 __func__, ret, (void *)dma_buf->dma_tag,
666711bcba0SDavid C Somayajulu                 (void *)dma_buf->dma_map, (void *)dma_buf->dma_b,
667711bcba0SDavid C Somayajulu 		dma_buf->size));
668711bcba0SDavid C Somayajulu 
669711bcba0SDavid C Somayajulu         return ret;
670711bcba0SDavid C Somayajulu }
671711bcba0SDavid C Somayajulu 
672711bcba0SDavid C Somayajulu void
673711bcba0SDavid C Somayajulu qls_free_dmabuf(qla_host_t *ha, qla_dma_t *dma_buf)
674711bcba0SDavid C Somayajulu {
675aeeb653cSJohn Baldwin         bus_dmamap_unload(dma_buf->dma_tag, dma_buf->dma_map);
676711bcba0SDavid C Somayajulu         bus_dmamem_free(dma_buf->dma_tag, dma_buf->dma_b, dma_buf->dma_map);
677711bcba0SDavid C Somayajulu         bus_dma_tag_destroy(dma_buf->dma_tag);
678711bcba0SDavid C Somayajulu }
679711bcba0SDavid C Somayajulu 
680711bcba0SDavid C Somayajulu static int
681711bcba0SDavid C Somayajulu qls_alloc_parent_dma_tag(qla_host_t *ha)
682711bcba0SDavid C Somayajulu {
683711bcba0SDavid C Somayajulu 	int		ret;
684711bcba0SDavid C Somayajulu 	device_t	dev;
685711bcba0SDavid C Somayajulu 
686711bcba0SDavid C Somayajulu 	dev = ha->pci_dev;
687711bcba0SDavid C Somayajulu 
688711bcba0SDavid C Somayajulu         /*
689711bcba0SDavid C Somayajulu          * Allocate parent DMA Tag
690711bcba0SDavid C Somayajulu          */
691711bcba0SDavid C Somayajulu         ret = bus_dma_tag_create(
692711bcba0SDavid C Somayajulu                         bus_get_dma_tag(dev),   /* parent */
693711bcba0SDavid C Somayajulu                         1,((bus_size_t)(1ULL << 32)),/* alignment, boundary */
694711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXADDR,      /* lowaddr */
695711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXADDR,      /* highaddr */
696711bcba0SDavid C Somayajulu                         NULL, NULL,             /* filter, filterarg */
697711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
698711bcba0SDavid C Somayajulu                         0,                      /* nsegments */
699711bcba0SDavid C Somayajulu                         BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
700711bcba0SDavid C Somayajulu                         0,                      /* flags */
701711bcba0SDavid C Somayajulu                         NULL, NULL,             /* lockfunc, lockarg */
702711bcba0SDavid C Somayajulu                         &ha->parent_tag);
703711bcba0SDavid C Somayajulu 
704711bcba0SDavid C Somayajulu         if (ret) {
705711bcba0SDavid C Somayajulu                 device_printf(dev, "%s: could not create parent dma tag\n",
706711bcba0SDavid C Somayajulu                         __func__);
707711bcba0SDavid C Somayajulu 		return (-1);
708711bcba0SDavid C Somayajulu         }
709711bcba0SDavid C Somayajulu 
710711bcba0SDavid C Somayajulu         ha->flags.parent_tag = 1;
711711bcba0SDavid C Somayajulu 
712711bcba0SDavid C Somayajulu 	return (0);
713711bcba0SDavid C Somayajulu }
714711bcba0SDavid C Somayajulu 
715711bcba0SDavid C Somayajulu static void
716711bcba0SDavid C Somayajulu qls_free_parent_dma_tag(qla_host_t *ha)
717711bcba0SDavid C Somayajulu {
718711bcba0SDavid C Somayajulu         if (ha->flags.parent_tag) {
719711bcba0SDavid C Somayajulu                 bus_dma_tag_destroy(ha->parent_tag);
720711bcba0SDavid C Somayajulu                 ha->flags.parent_tag = 0;
721711bcba0SDavid C Somayajulu         }
722711bcba0SDavid C Somayajulu }
723711bcba0SDavid C Somayajulu 
724711bcba0SDavid C Somayajulu /*
725711bcba0SDavid C Somayajulu  * Name: qls_init_ifnet
726711bcba0SDavid C Somayajulu  * Function: Creates the Network Device Interface and Registers it with the O.S
727711bcba0SDavid C Somayajulu  */
728711bcba0SDavid C Somayajulu 
729711bcba0SDavid C Somayajulu static void
730711bcba0SDavid C Somayajulu qls_init_ifnet(device_t dev, qla_host_t *ha)
731711bcba0SDavid C Somayajulu {
732711bcba0SDavid C Somayajulu 	struct ifnet *ifp;
733711bcba0SDavid C Somayajulu 
734711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: enter\n", __func__));
735711bcba0SDavid C Somayajulu 
736711bcba0SDavid C Somayajulu 	ifp = ha->ifp = if_alloc(IFT_ETHER);
737711bcba0SDavid C Somayajulu 
738711bcba0SDavid C Somayajulu 	if (ifp == NULL)
739711bcba0SDavid C Somayajulu 		panic("%s: cannot if_alloc()\n", device_get_nameunit(dev));
740711bcba0SDavid C Somayajulu 
741711bcba0SDavid C Somayajulu 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
742b245f96cSGleb Smirnoff 	ifp->if_baudrate = IF_Gbps(10);
743711bcba0SDavid C Somayajulu 	ifp->if_init = qls_init;
744711bcba0SDavid C Somayajulu 	ifp->if_softc = ha;
745711bcba0SDavid C Somayajulu 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
746711bcba0SDavid C Somayajulu 	ifp->if_ioctl = qls_ioctl;
747711bcba0SDavid C Somayajulu 	ifp->if_start = qls_start;
748711bcba0SDavid C Somayajulu 
749711bcba0SDavid C Somayajulu 	IFQ_SET_MAXLEN(&ifp->if_snd, qls_get_ifq_snd_maxlen(ha));
750711bcba0SDavid C Somayajulu 	ifp->if_snd.ifq_drv_maxlen = qls_get_ifq_snd_maxlen(ha);
751711bcba0SDavid C Somayajulu 	IFQ_SET_READY(&ifp->if_snd);
752711bcba0SDavid C Somayajulu 
753711bcba0SDavid C Somayajulu 	ha->max_frame_size = ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
754711bcba0SDavid C Somayajulu 	if (ha->max_frame_size <= MCLBYTES) {
755711bcba0SDavid C Somayajulu 		ha->msize = MCLBYTES;
756711bcba0SDavid C Somayajulu 	} else if (ha->max_frame_size <= MJUMPAGESIZE) {
757711bcba0SDavid C Somayajulu 		ha->msize = MJUMPAGESIZE;
758711bcba0SDavid C Somayajulu 	} else
759711bcba0SDavid C Somayajulu 		ha->msize = MJUM9BYTES;
760711bcba0SDavid C Somayajulu 
761711bcba0SDavid C Somayajulu 	ether_ifattach(ifp, qls_get_mac_addr(ha));
762711bcba0SDavid C Somayajulu 
763711bcba0SDavid C Somayajulu 	ifp->if_capabilities = IFCAP_JUMBO_MTU;
764711bcba0SDavid C Somayajulu 
765711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_HWCSUM;
766711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
767711bcba0SDavid C Somayajulu 
768711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_TSO4;
769711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
770711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_VLAN_HWTSO;
771711bcba0SDavid C Somayajulu 	ifp->if_capabilities |= IFCAP_LINKSTATE;
772711bcba0SDavid C Somayajulu 
773711bcba0SDavid C Somayajulu 	ifp->if_capenable = ifp->if_capabilities;
774711bcba0SDavid C Somayajulu 
7751bffa951SGleb Smirnoff 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
776711bcba0SDavid C Somayajulu 
777711bcba0SDavid C Somayajulu 	ifmedia_init(&ha->media, IFM_IMASK, qls_media_change, qls_media_status);
778711bcba0SDavid C Somayajulu 
779711bcba0SDavid C Somayajulu 	ifmedia_add(&ha->media, (IFM_ETHER | qls_get_optics(ha) | IFM_FDX), 0,
780711bcba0SDavid C Somayajulu 		NULL);
781711bcba0SDavid C Somayajulu 	ifmedia_add(&ha->media, (IFM_ETHER | IFM_AUTO), 0, NULL);
782711bcba0SDavid C Somayajulu 
783711bcba0SDavid C Somayajulu 	ifmedia_set(&ha->media, (IFM_ETHER | IFM_AUTO));
784711bcba0SDavid C Somayajulu 
785711bcba0SDavid C Somayajulu 	QL_DPRINT2((dev, "%s: exit\n", __func__));
786711bcba0SDavid C Somayajulu 
787711bcba0SDavid C Somayajulu 	return;
788711bcba0SDavid C Somayajulu }
789711bcba0SDavid C Somayajulu 
790711bcba0SDavid C Somayajulu static void
791711bcba0SDavid C Somayajulu qls_init_locked(qla_host_t *ha)
792711bcba0SDavid C Somayajulu {
793711bcba0SDavid C Somayajulu 	struct ifnet *ifp = ha->ifp;
794711bcba0SDavid C Somayajulu 
795711bcba0SDavid C Somayajulu 	qls_stop(ha);
796711bcba0SDavid C Somayajulu 
797711bcba0SDavid C Somayajulu 	qls_flush_xmt_bufs(ha);
798711bcba0SDavid C Somayajulu 
799711bcba0SDavid C Somayajulu 	if (qls_alloc_rcv_bufs(ha) != 0)
800711bcba0SDavid C Somayajulu 		return;
801711bcba0SDavid C Somayajulu 
802711bcba0SDavid C Somayajulu 	if (qls_config_lro(ha))
803711bcba0SDavid C Somayajulu 		return;
804711bcba0SDavid C Somayajulu 
805711bcba0SDavid C Somayajulu 	bcopy(IF_LLADDR(ha->ifp), ha->mac_addr, ETHER_ADDR_LEN);
806711bcba0SDavid C Somayajulu 
807711bcba0SDavid C Somayajulu 	ifp->if_hwassist = CSUM_IP;
808711bcba0SDavid C Somayajulu 	ifp->if_hwassist |= CSUM_TCP;
809711bcba0SDavid C Somayajulu 	ifp->if_hwassist |= CSUM_UDP;
810711bcba0SDavid C Somayajulu 	ifp->if_hwassist |= CSUM_TSO;
811711bcba0SDavid C Somayajulu 
812711bcba0SDavid C Somayajulu  	if (qls_init_hw_if(ha) == 0) {
813711bcba0SDavid C Somayajulu 		ifp = ha->ifp;
814711bcba0SDavid C Somayajulu 		ifp->if_drv_flags |= IFF_DRV_RUNNING;
815711bcba0SDavid C Somayajulu 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
816711bcba0SDavid C Somayajulu 		ha->flags.qla_watchdog_pause = 0;
817711bcba0SDavid C Somayajulu 	}
818711bcba0SDavid C Somayajulu 
819711bcba0SDavid C Somayajulu 	return;
820711bcba0SDavid C Somayajulu }
821711bcba0SDavid C Somayajulu 
822711bcba0SDavid C Somayajulu static void
823711bcba0SDavid C Somayajulu qls_init(void *arg)
824711bcba0SDavid C Somayajulu {
825711bcba0SDavid C Somayajulu 	qla_host_t *ha;
826711bcba0SDavid C Somayajulu 
827711bcba0SDavid C Somayajulu 	ha = (qla_host_t *)arg;
828711bcba0SDavid C Somayajulu 
829711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: enter\n", __func__));
830711bcba0SDavid C Somayajulu 
831711bcba0SDavid C Somayajulu 	(void)QLA_LOCK(ha, __func__, 0);
832711bcba0SDavid C Somayajulu 	qls_init_locked(ha);
833711bcba0SDavid C Somayajulu 	QLA_UNLOCK(ha, __func__);
834711bcba0SDavid C Somayajulu 
835711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: exit\n", __func__));
836711bcba0SDavid C Somayajulu }
837711bcba0SDavid C Somayajulu 
8386dfd0870SGleb Smirnoff static u_int
8396dfd0870SGleb Smirnoff qls_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int mcnt)
8406dfd0870SGleb Smirnoff {
8416dfd0870SGleb Smirnoff 	uint8_t *mta = arg;
8426dfd0870SGleb Smirnoff 
8436dfd0870SGleb Smirnoff 	if (mcnt == Q8_MAX_NUM_MULTICAST_ADDRS)
8446dfd0870SGleb Smirnoff 		return (0);
8456dfd0870SGleb Smirnoff 
8466dfd0870SGleb Smirnoff 	bcopy(LLADDR(sdl), &mta[mcnt * Q8_MAC_ADDR_LEN], Q8_MAC_ADDR_LEN);
8476dfd0870SGleb Smirnoff 
8486dfd0870SGleb Smirnoff 	return (1);
8496dfd0870SGleb Smirnoff }
8506dfd0870SGleb Smirnoff 
851711bcba0SDavid C Somayajulu static void
852711bcba0SDavid C Somayajulu qls_set_multi(qla_host_t *ha, uint32_t add_multi)
853711bcba0SDavid C Somayajulu {
854711bcba0SDavid C Somayajulu 	uint8_t mta[Q8_MAX_NUM_MULTICAST_ADDRS * Q8_MAC_ADDR_LEN];
855711bcba0SDavid C Somayajulu 	struct ifnet *ifp = ha->ifp;
8566dfd0870SGleb Smirnoff 	int mcnt;
857711bcba0SDavid C Somayajulu 
8586dfd0870SGleb Smirnoff 	mcnt = if_foreach_llmaddr(ifp, qls_copy_maddr, mta);
859711bcba0SDavid C Somayajulu 
860711bcba0SDavid C Somayajulu 	if (QLA_LOCK(ha, __func__, 1) == 0) {
861711bcba0SDavid C Somayajulu 		qls_hw_set_multi(ha, mta, mcnt, add_multi);
862711bcba0SDavid C Somayajulu 		QLA_UNLOCK(ha, __func__);
863711bcba0SDavid C Somayajulu 	}
864711bcba0SDavid C Somayajulu 
865711bcba0SDavid C Somayajulu 	return;
866711bcba0SDavid C Somayajulu }
867711bcba0SDavid C Somayajulu 
868711bcba0SDavid C Somayajulu static int
869711bcba0SDavid C Somayajulu qls_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
870711bcba0SDavid C Somayajulu {
871711bcba0SDavid C Somayajulu 	int ret = 0;
872711bcba0SDavid C Somayajulu 	struct ifreq *ifr = (struct ifreq *)data;
873711bcba0SDavid C Somayajulu 	struct ifaddr *ifa = (struct ifaddr *)data;
874711bcba0SDavid C Somayajulu 	qla_host_t *ha;
875711bcba0SDavid C Somayajulu 
876711bcba0SDavid C Somayajulu 	ha = (qla_host_t *)ifp->if_softc;
877711bcba0SDavid C Somayajulu 
878711bcba0SDavid C Somayajulu 	switch (cmd) {
879711bcba0SDavid C Somayajulu 	case SIOCSIFADDR:
880711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev, "%s: SIOCSIFADDR (0x%lx)\n",
881711bcba0SDavid C Somayajulu 			__func__, cmd));
882711bcba0SDavid C Somayajulu 
883711bcba0SDavid C Somayajulu 		if (ifa->ifa_addr->sa_family == AF_INET) {
884711bcba0SDavid C Somayajulu 			ifp->if_flags |= IFF_UP;
885711bcba0SDavid C Somayajulu 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
886711bcba0SDavid C Somayajulu 				(void)QLA_LOCK(ha, __func__, 0);
887711bcba0SDavid C Somayajulu 				qls_init_locked(ha);
888711bcba0SDavid C Somayajulu 				QLA_UNLOCK(ha, __func__);
889711bcba0SDavid C Somayajulu 			}
890711bcba0SDavid C Somayajulu 			QL_DPRINT4((ha->pci_dev,
891711bcba0SDavid C Somayajulu 				"%s: SIOCSIFADDR (0x%lx) ipv4 [0x%08x]\n",
892711bcba0SDavid C Somayajulu 				__func__, cmd,
893711bcba0SDavid C Somayajulu 				ntohl(IA_SIN(ifa)->sin_addr.s_addr)));
894711bcba0SDavid C Somayajulu 
895711bcba0SDavid C Somayajulu 			arp_ifinit(ifp, ifa);
896711bcba0SDavid C Somayajulu 		} else {
897711bcba0SDavid C Somayajulu 			ether_ioctl(ifp, cmd, data);
898711bcba0SDavid C Somayajulu 		}
899711bcba0SDavid C Somayajulu 		break;
900711bcba0SDavid C Somayajulu 
901711bcba0SDavid C Somayajulu 	case SIOCSIFMTU:
902711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev, "%s: SIOCSIFMTU (0x%lx)\n",
903711bcba0SDavid C Somayajulu 			__func__, cmd));
904711bcba0SDavid C Somayajulu 
905711bcba0SDavid C Somayajulu 		if (ifr->ifr_mtu > QLA_MAX_MTU) {
906711bcba0SDavid C Somayajulu 			ret = EINVAL;
907711bcba0SDavid C Somayajulu 		} else {
908711bcba0SDavid C Somayajulu 			(void) QLA_LOCK(ha, __func__, 0);
909711bcba0SDavid C Somayajulu 
910711bcba0SDavid C Somayajulu 			ifp->if_mtu = ifr->ifr_mtu;
911711bcba0SDavid C Somayajulu 			ha->max_frame_size =
912711bcba0SDavid C Somayajulu 				ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
913711bcba0SDavid C Somayajulu 
914711bcba0SDavid C Somayajulu 			QLA_UNLOCK(ha, __func__);
915711bcba0SDavid C Somayajulu 
916711bcba0SDavid C Somayajulu 			if (ret)
917711bcba0SDavid C Somayajulu 				ret = EINVAL;
918711bcba0SDavid C Somayajulu 		}
919711bcba0SDavid C Somayajulu 
920711bcba0SDavid C Somayajulu 		break;
921711bcba0SDavid C Somayajulu 
922711bcba0SDavid C Somayajulu 	case SIOCSIFFLAGS:
923711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev, "%s: SIOCSIFFLAGS (0x%lx)\n",
924711bcba0SDavid C Somayajulu 			__func__, cmd));
925711bcba0SDavid C Somayajulu 
926711bcba0SDavid C Somayajulu 		(void)QLA_LOCK(ha, __func__, 0);
927711bcba0SDavid C Somayajulu 
928711bcba0SDavid C Somayajulu 		if (ifp->if_flags & IFF_UP) {
929711bcba0SDavid C Somayajulu 			if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) {
930711bcba0SDavid C Somayajulu 				if ((ifp->if_flags ^ ha->if_flags) &
931711bcba0SDavid C Somayajulu 					IFF_PROMISC) {
932711bcba0SDavid C Somayajulu 					ret = qls_set_promisc(ha);
933711bcba0SDavid C Somayajulu 				} else if ((ifp->if_flags ^ ha->if_flags) &
934711bcba0SDavid C Somayajulu 					IFF_ALLMULTI) {
935711bcba0SDavid C Somayajulu 					ret = qls_set_allmulti(ha);
936711bcba0SDavid C Somayajulu 				}
937711bcba0SDavid C Somayajulu 			} else {
938711bcba0SDavid C Somayajulu 				ha->max_frame_size = ifp->if_mtu +
939711bcba0SDavid C Somayajulu 					ETHER_HDR_LEN + ETHER_CRC_LEN;
940711bcba0SDavid C Somayajulu 				qls_init_locked(ha);
941711bcba0SDavid C Somayajulu 			}
942711bcba0SDavid C Somayajulu 		} else {
943711bcba0SDavid C Somayajulu 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
944711bcba0SDavid C Somayajulu 				qls_stop(ha);
945711bcba0SDavid C Somayajulu 			ha->if_flags = ifp->if_flags;
946711bcba0SDavid C Somayajulu 		}
947711bcba0SDavid C Somayajulu 
948711bcba0SDavid C Somayajulu 		QLA_UNLOCK(ha, __func__);
949711bcba0SDavid C Somayajulu 		break;
950711bcba0SDavid C Somayajulu 
951711bcba0SDavid C Somayajulu 	case SIOCADDMULTI:
952711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev,
953711bcba0SDavid C Somayajulu 			"%s: %s (0x%lx)\n", __func__, "SIOCADDMULTI", cmd));
954711bcba0SDavid C Somayajulu 
955711bcba0SDavid C Somayajulu 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
956711bcba0SDavid C Somayajulu 			qls_set_multi(ha, 1);
957711bcba0SDavid C Somayajulu 		}
958711bcba0SDavid C Somayajulu 		break;
959711bcba0SDavid C Somayajulu 
960711bcba0SDavid C Somayajulu 	case SIOCDELMULTI:
961711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev,
962711bcba0SDavid C Somayajulu 			"%s: %s (0x%lx)\n", __func__, "SIOCDELMULTI", cmd));
963711bcba0SDavid C Somayajulu 
964711bcba0SDavid C Somayajulu 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
965711bcba0SDavid C Somayajulu 			qls_set_multi(ha, 0);
966711bcba0SDavid C Somayajulu 		}
967711bcba0SDavid C Somayajulu 		break;
968711bcba0SDavid C Somayajulu 
969711bcba0SDavid C Somayajulu 	case SIOCSIFMEDIA:
970711bcba0SDavid C Somayajulu 	case SIOCGIFMEDIA:
971711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev,
972711bcba0SDavid C Somayajulu 			"%s: SIOCSIFMEDIA/SIOCGIFMEDIA (0x%lx)\n",
973711bcba0SDavid C Somayajulu 			__func__, cmd));
974711bcba0SDavid C Somayajulu 		ret = ifmedia_ioctl(ifp, ifr, &ha->media, cmd);
975711bcba0SDavid C Somayajulu 		break;
976711bcba0SDavid C Somayajulu 
977711bcba0SDavid C Somayajulu 	case SIOCSIFCAP:
978711bcba0SDavid C Somayajulu 	{
979711bcba0SDavid C Somayajulu 		int mask = ifr->ifr_reqcap ^ ifp->if_capenable;
980711bcba0SDavid C Somayajulu 
981711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev, "%s: SIOCSIFCAP (0x%lx)\n",
982711bcba0SDavid C Somayajulu 			__func__, cmd));
983711bcba0SDavid C Somayajulu 
984711bcba0SDavid C Somayajulu 		if (mask & IFCAP_HWCSUM)
985711bcba0SDavid C Somayajulu 			ifp->if_capenable ^= IFCAP_HWCSUM;
986711bcba0SDavid C Somayajulu 		if (mask & IFCAP_TSO4)
987711bcba0SDavid C Somayajulu 			ifp->if_capenable ^= IFCAP_TSO4;
988711bcba0SDavid C Somayajulu 		if (mask & IFCAP_VLAN_HWTAGGING)
989711bcba0SDavid C Somayajulu 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
990711bcba0SDavid C Somayajulu 		if (mask & IFCAP_VLAN_HWTSO)
991711bcba0SDavid C Somayajulu 			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
992711bcba0SDavid C Somayajulu 
993711bcba0SDavid C Somayajulu 		if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
994711bcba0SDavid C Somayajulu 			qls_init(ha);
995711bcba0SDavid C Somayajulu 
996711bcba0SDavid C Somayajulu 		VLAN_CAPABILITIES(ifp);
997711bcba0SDavid C Somayajulu 		break;
998711bcba0SDavid C Somayajulu 	}
999711bcba0SDavid C Somayajulu 
1000711bcba0SDavid C Somayajulu 	default:
1001711bcba0SDavid C Somayajulu 		QL_DPRINT4((ha->pci_dev, "%s: default (0x%lx)\n",
1002711bcba0SDavid C Somayajulu 			__func__, cmd));
1003711bcba0SDavid C Somayajulu 		ret = ether_ioctl(ifp, cmd, data);
1004711bcba0SDavid C Somayajulu 		break;
1005711bcba0SDavid C Somayajulu 	}
1006711bcba0SDavid C Somayajulu 
1007711bcba0SDavid C Somayajulu 	return (ret);
1008711bcba0SDavid C Somayajulu }
1009711bcba0SDavid C Somayajulu 
1010711bcba0SDavid C Somayajulu static int
1011711bcba0SDavid C Somayajulu qls_media_change(struct ifnet *ifp)
1012711bcba0SDavid C Somayajulu {
1013711bcba0SDavid C Somayajulu 	qla_host_t *ha;
1014711bcba0SDavid C Somayajulu 	struct ifmedia *ifm;
1015711bcba0SDavid C Somayajulu 	int ret = 0;
1016711bcba0SDavid C Somayajulu 
1017711bcba0SDavid C Somayajulu 	ha = (qla_host_t *)ifp->if_softc;
1018711bcba0SDavid C Somayajulu 
1019711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: enter\n", __func__));
1020711bcba0SDavid C Somayajulu 
1021711bcba0SDavid C Somayajulu 	ifm = &ha->media;
1022711bcba0SDavid C Somayajulu 
1023711bcba0SDavid C Somayajulu 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
1024711bcba0SDavid C Somayajulu 		ret = EINVAL;
1025711bcba0SDavid C Somayajulu 
1026711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: exit\n", __func__));
1027711bcba0SDavid C Somayajulu 
1028711bcba0SDavid C Somayajulu 	return (ret);
1029711bcba0SDavid C Somayajulu }
1030711bcba0SDavid C Somayajulu 
1031711bcba0SDavid C Somayajulu static void
1032711bcba0SDavid C Somayajulu qls_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
1033711bcba0SDavid C Somayajulu {
1034711bcba0SDavid C Somayajulu 	qla_host_t *ha;
1035711bcba0SDavid C Somayajulu 
1036711bcba0SDavid C Somayajulu 	ha = (qla_host_t *)ifp->if_softc;
1037711bcba0SDavid C Somayajulu 
1038711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: enter\n", __func__));
1039711bcba0SDavid C Somayajulu 
1040711bcba0SDavid C Somayajulu 	ifmr->ifm_status = IFM_AVALID;
1041711bcba0SDavid C Somayajulu 	ifmr->ifm_active = IFM_ETHER;
1042711bcba0SDavid C Somayajulu 
1043711bcba0SDavid C Somayajulu 	qls_update_link_state(ha);
1044711bcba0SDavid C Somayajulu 	if (ha->link_up) {
1045711bcba0SDavid C Somayajulu 		ifmr->ifm_status |= IFM_ACTIVE;
1046711bcba0SDavid C Somayajulu 		ifmr->ifm_active |= (IFM_FDX | qls_get_optics(ha));
1047711bcba0SDavid C Somayajulu 	}
1048711bcba0SDavid C Somayajulu 
1049711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: exit (%s)\n", __func__,\
1050711bcba0SDavid C Somayajulu 		(ha->link_up ? "link_up" : "link_down")));
1051711bcba0SDavid C Somayajulu 
1052711bcba0SDavid C Somayajulu 	return;
1053711bcba0SDavid C Somayajulu }
1054711bcba0SDavid C Somayajulu 
1055711bcba0SDavid C Somayajulu static void
1056711bcba0SDavid C Somayajulu qls_start(struct ifnet *ifp)
1057711bcba0SDavid C Somayajulu {
1058711bcba0SDavid C Somayajulu 	int		i, ret = 0;
1059711bcba0SDavid C Somayajulu 	struct mbuf	*m_head;
1060711bcba0SDavid C Somayajulu 	qla_host_t	*ha = (qla_host_t *)ifp->if_softc;
1061711bcba0SDavid C Somayajulu 
1062711bcba0SDavid C Somayajulu 	QL_DPRINT8((ha->pci_dev, "%s: enter\n", __func__));
1063711bcba0SDavid C Somayajulu 
1064711bcba0SDavid C Somayajulu 	if (!mtx_trylock(&ha->tx_lock)) {
1065711bcba0SDavid C Somayajulu 		QL_DPRINT8((ha->pci_dev,
1066711bcba0SDavid C Somayajulu 			"%s: mtx_trylock(&ha->tx_lock) failed\n", __func__));
1067711bcba0SDavid C Somayajulu 		return;
1068711bcba0SDavid C Somayajulu 	}
1069711bcba0SDavid C Somayajulu 
1070711bcba0SDavid C Somayajulu 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) ==
1071711bcba0SDavid C Somayajulu 		IFF_DRV_RUNNING) {
1072711bcba0SDavid C Somayajulu 
1073711bcba0SDavid C Somayajulu 		for (i = 0; i < ha->num_tx_rings; i++) {
1074711bcba0SDavid C Somayajulu 			ret |= qls_hw_tx_done(ha, i);
1075711bcba0SDavid C Somayajulu 		}
1076711bcba0SDavid C Somayajulu 
1077711bcba0SDavid C Somayajulu 		if (ret == 0)
1078711bcba0SDavid C Somayajulu 			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1079711bcba0SDavid C Somayajulu 	}
1080711bcba0SDavid C Somayajulu 
1081711bcba0SDavid C Somayajulu 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
1082711bcba0SDavid C Somayajulu 		IFF_DRV_RUNNING) {
1083711bcba0SDavid C Somayajulu 		QL_DPRINT8((ha->pci_dev, "%s: !IFF_DRV_RUNNING\n", __func__));
1084711bcba0SDavid C Somayajulu 		QLA_TX_UNLOCK(ha);
1085711bcba0SDavid C Somayajulu 		return;
1086711bcba0SDavid C Somayajulu 	}
1087711bcba0SDavid C Somayajulu 
1088711bcba0SDavid C Somayajulu 	if (!ha->link_up) {
1089711bcba0SDavid C Somayajulu 		qls_update_link_state(ha);
1090711bcba0SDavid C Somayajulu 		if (!ha->link_up) {
1091711bcba0SDavid C Somayajulu 			QL_DPRINT8((ha->pci_dev, "%s: link down\n", __func__));
1092711bcba0SDavid C Somayajulu 			QLA_TX_UNLOCK(ha);
1093711bcba0SDavid C Somayajulu 			return;
1094711bcba0SDavid C Somayajulu 		}
1095711bcba0SDavid C Somayajulu 	}
1096711bcba0SDavid C Somayajulu 
1097711bcba0SDavid C Somayajulu 	while (ifp->if_snd.ifq_head != NULL) {
1098711bcba0SDavid C Somayajulu 
1099711bcba0SDavid C Somayajulu 		IF_DEQUEUE(&ifp->if_snd, m_head);
1100711bcba0SDavid C Somayajulu 
1101711bcba0SDavid C Somayajulu 		if (m_head == NULL) {
1102711bcba0SDavid C Somayajulu 			QL_DPRINT8((ha->pci_dev, "%s: m_head == NULL\n",
1103711bcba0SDavid C Somayajulu 				__func__));
1104711bcba0SDavid C Somayajulu 			break;
1105711bcba0SDavid C Somayajulu 		}
1106711bcba0SDavid C Somayajulu 
1107711bcba0SDavid C Somayajulu 		if (qls_send(ha, &m_head)) {
1108711bcba0SDavid C Somayajulu 			if (m_head == NULL)
1109711bcba0SDavid C Somayajulu 				break;
1110711bcba0SDavid C Somayajulu 			QL_DPRINT8((ha->pci_dev, "%s: PREPEND\n", __func__));
1111711bcba0SDavid C Somayajulu 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1112711bcba0SDavid C Somayajulu 			IF_PREPEND(&ifp->if_snd, m_head);
1113711bcba0SDavid C Somayajulu 			break;
1114711bcba0SDavid C Somayajulu 		}
1115711bcba0SDavid C Somayajulu 		/* Send a copy of the frame to the BPF listener */
1116711bcba0SDavid C Somayajulu 		ETHER_BPF_MTAP(ifp, m_head);
1117711bcba0SDavid C Somayajulu 	}
1118711bcba0SDavid C Somayajulu 
1119711bcba0SDavid C Somayajulu 	QLA_TX_UNLOCK(ha);
1120711bcba0SDavid C Somayajulu 	QL_DPRINT8((ha->pci_dev, "%s: exit\n", __func__));
1121711bcba0SDavid C Somayajulu 	return;
1122711bcba0SDavid C Somayajulu }
1123711bcba0SDavid C Somayajulu 
1124711bcba0SDavid C Somayajulu static int
1125711bcba0SDavid C Somayajulu qls_send(qla_host_t *ha, struct mbuf **m_headp)
1126711bcba0SDavid C Somayajulu {
1127711bcba0SDavid C Somayajulu 	bus_dma_segment_t	segs[QLA_MAX_SEGMENTS];
1128711bcba0SDavid C Somayajulu 	bus_dmamap_t		map;
1129711bcba0SDavid C Somayajulu 	int			nsegs;
1130711bcba0SDavid C Somayajulu 	int			ret = -1;
1131711bcba0SDavid C Somayajulu 	uint32_t		tx_idx;
1132711bcba0SDavid C Somayajulu 	struct mbuf		*m_head = *m_headp;
1133711bcba0SDavid C Somayajulu 	uint32_t		txr_idx = 0;
1134711bcba0SDavid C Somayajulu 
1135711bcba0SDavid C Somayajulu 	QL_DPRINT8((ha->pci_dev, "%s: enter\n", __func__));
1136711bcba0SDavid C Somayajulu 
1137c2529042SHans Petter Selasky 	/* check if flowid is set */
1138c2529042SHans Petter Selasky 	if (M_HASHTYPE_GET(m_head) != M_HASHTYPE_NONE)
1139711bcba0SDavid C Somayajulu 		txr_idx = m_head->m_pkthdr.flowid & (ha->num_tx_rings - 1);
1140711bcba0SDavid C Somayajulu 
1141711bcba0SDavid C Somayajulu 	tx_idx = ha->tx_ring[txr_idx].txr_next;
1142711bcba0SDavid C Somayajulu 
1143711bcba0SDavid C Somayajulu 	map = ha->tx_ring[txr_idx].tx_buf[tx_idx].map;
1144711bcba0SDavid C Somayajulu 
1145711bcba0SDavid C Somayajulu 	ret = bus_dmamap_load_mbuf_sg(ha->tx_tag, map, m_head, segs, &nsegs,
1146711bcba0SDavid C Somayajulu 			BUS_DMA_NOWAIT);
1147711bcba0SDavid C Somayajulu 
1148711bcba0SDavid C Somayajulu 	if (ret == EFBIG) {
1149711bcba0SDavid C Somayajulu 
1150711bcba0SDavid C Somayajulu 		struct mbuf *m;
1151711bcba0SDavid C Somayajulu 
1152711bcba0SDavid C Somayajulu 		QL_DPRINT8((ha->pci_dev, "%s: EFBIG [%d]\n", __func__,
1153711bcba0SDavid C Somayajulu 			m_head->m_pkthdr.len));
1154711bcba0SDavid C Somayajulu 
1155b8c83a19SGleb Smirnoff 		m = m_defrag(m_head, M_NOWAIT);
1156711bcba0SDavid C Somayajulu 		if (m == NULL) {
1157711bcba0SDavid C Somayajulu 			ha->err_tx_defrag++;
1158711bcba0SDavid C Somayajulu 			m_freem(m_head);
1159711bcba0SDavid C Somayajulu 			*m_headp = NULL;
1160711bcba0SDavid C Somayajulu 			device_printf(ha->pci_dev,
1161711bcba0SDavid C Somayajulu 				"%s: m_defrag() = NULL [%d]\n",
1162711bcba0SDavid C Somayajulu 				__func__, ret);
1163711bcba0SDavid C Somayajulu 			return (ENOBUFS);
1164711bcba0SDavid C Somayajulu 		}
1165711bcba0SDavid C Somayajulu 		m_head = m;
1166711bcba0SDavid C Somayajulu 		*m_headp = m_head;
1167711bcba0SDavid C Somayajulu 
1168711bcba0SDavid C Somayajulu 		if ((ret = bus_dmamap_load_mbuf_sg(ha->tx_tag, map, m_head,
1169711bcba0SDavid C Somayajulu 					segs, &nsegs, BUS_DMA_NOWAIT))) {
1170711bcba0SDavid C Somayajulu 
1171711bcba0SDavid C Somayajulu 			ha->err_tx_dmamap_load++;
1172711bcba0SDavid C Somayajulu 
1173711bcba0SDavid C Somayajulu 			device_printf(ha->pci_dev,
1174711bcba0SDavid C Somayajulu 				"%s: bus_dmamap_load_mbuf_sg failed0[%d, %d]\n",
1175711bcba0SDavid C Somayajulu 				__func__, ret, m_head->m_pkthdr.len);
1176711bcba0SDavid C Somayajulu 
1177711bcba0SDavid C Somayajulu 			if (ret != ENOMEM) {
1178711bcba0SDavid C Somayajulu 				m_freem(m_head);
1179711bcba0SDavid C Somayajulu 				*m_headp = NULL;
1180711bcba0SDavid C Somayajulu 			}
1181711bcba0SDavid C Somayajulu 			return (ret);
1182711bcba0SDavid C Somayajulu 		}
1183711bcba0SDavid C Somayajulu 
1184711bcba0SDavid C Somayajulu 	} else if (ret) {
1185711bcba0SDavid C Somayajulu 
1186711bcba0SDavid C Somayajulu 		ha->err_tx_dmamap_load++;
1187711bcba0SDavid C Somayajulu 
1188711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev,
1189711bcba0SDavid C Somayajulu 			"%s: bus_dmamap_load_mbuf_sg failed1[%d, %d]\n",
1190711bcba0SDavid C Somayajulu 			__func__, ret, m_head->m_pkthdr.len);
1191711bcba0SDavid C Somayajulu 
1192711bcba0SDavid C Somayajulu 		if (ret != ENOMEM) {
1193711bcba0SDavid C Somayajulu 			m_freem(m_head);
1194711bcba0SDavid C Somayajulu 			*m_headp = NULL;
1195711bcba0SDavid C Somayajulu 		}
1196711bcba0SDavid C Somayajulu 		return (ret);
1197711bcba0SDavid C Somayajulu 	}
1198711bcba0SDavid C Somayajulu 
1199711bcba0SDavid C Somayajulu 	QL_ASSERT(ha, (nsegs != 0), ("qls_send: empty packet"));
1200711bcba0SDavid C Somayajulu 
1201711bcba0SDavid C Somayajulu 	bus_dmamap_sync(ha->tx_tag, map, BUS_DMASYNC_PREWRITE);
1202711bcba0SDavid C Somayajulu 
1203711bcba0SDavid C Somayajulu         if (!(ret = qls_hw_send(ha, segs, nsegs, tx_idx, m_head, txr_idx))) {
1204711bcba0SDavid C Somayajulu 
1205711bcba0SDavid C Somayajulu 		ha->tx_ring[txr_idx].count++;
1206711bcba0SDavid C Somayajulu 		ha->tx_ring[txr_idx].tx_buf[tx_idx].m_head = m_head;
1207711bcba0SDavid C Somayajulu 		ha->tx_ring[txr_idx].tx_buf[tx_idx].map = map;
1208711bcba0SDavid C Somayajulu 	} else {
1209711bcba0SDavid C Somayajulu 		if (ret == EINVAL) {
1210711bcba0SDavid C Somayajulu 			if (m_head)
1211711bcba0SDavid C Somayajulu 				m_freem(m_head);
1212711bcba0SDavid C Somayajulu 			*m_headp = NULL;
1213711bcba0SDavid C Somayajulu 		}
1214711bcba0SDavid C Somayajulu 	}
1215711bcba0SDavid C Somayajulu 
1216711bcba0SDavid C Somayajulu 	QL_DPRINT8((ha->pci_dev, "%s: exit\n", __func__));
1217711bcba0SDavid C Somayajulu 	return (ret);
1218711bcba0SDavid C Somayajulu }
1219711bcba0SDavid C Somayajulu 
1220711bcba0SDavid C Somayajulu static void
1221711bcba0SDavid C Somayajulu qls_stop(qla_host_t *ha)
1222711bcba0SDavid C Somayajulu {
1223711bcba0SDavid C Somayajulu 	struct ifnet *ifp = ha->ifp;
1224711bcba0SDavid C Somayajulu 	device_t	dev;
1225711bcba0SDavid C Somayajulu 
1226711bcba0SDavid C Somayajulu 	dev = ha->pci_dev;
1227711bcba0SDavid C Somayajulu 
1228711bcba0SDavid C Somayajulu 	ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
1229711bcba0SDavid C Somayajulu 
1230711bcba0SDavid C Somayajulu 	ha->flags.qla_watchdog_pause = 1;
1231711bcba0SDavid C Somayajulu 
1232711bcba0SDavid C Somayajulu 	while (!ha->qla_watchdog_paused)
1233711bcba0SDavid C Somayajulu 		qls_mdelay(__func__, 1);
1234711bcba0SDavid C Somayajulu 
1235711bcba0SDavid C Somayajulu 	qls_del_hw_if(ha);
1236711bcba0SDavid C Somayajulu 
1237711bcba0SDavid C Somayajulu 	qls_free_lro(ha);
1238711bcba0SDavid C Somayajulu 
1239711bcba0SDavid C Somayajulu 	qls_flush_xmt_bufs(ha);
1240711bcba0SDavid C Somayajulu 	qls_free_rcv_bufs(ha);
1241711bcba0SDavid C Somayajulu 
1242711bcba0SDavid C Somayajulu 	return;
1243711bcba0SDavid C Somayajulu }
1244711bcba0SDavid C Somayajulu 
1245711bcba0SDavid C Somayajulu /*
1246711bcba0SDavid C Somayajulu  * Buffer Management Functions for Transmit and Receive Rings
1247711bcba0SDavid C Somayajulu  */
1248711bcba0SDavid C Somayajulu /*
1249711bcba0SDavid C Somayajulu  * Release mbuf after it sent on the wire
1250711bcba0SDavid C Somayajulu  */
1251711bcba0SDavid C Somayajulu static void
1252711bcba0SDavid C Somayajulu qls_flush_tx_buf(qla_host_t *ha, qla_tx_buf_t *txb)
1253711bcba0SDavid C Somayajulu {
1254711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: enter\n", __func__));
1255711bcba0SDavid C Somayajulu 
1256711bcba0SDavid C Somayajulu 	if (txb->m_head) {
1257711bcba0SDavid C Somayajulu 
1258711bcba0SDavid C Somayajulu 		bus_dmamap_unload(ha->tx_tag, txb->map);
1259711bcba0SDavid C Somayajulu 
1260711bcba0SDavid C Somayajulu 		m_freem(txb->m_head);
1261711bcba0SDavid C Somayajulu 		txb->m_head = NULL;
1262711bcba0SDavid C Somayajulu 	}
1263711bcba0SDavid C Somayajulu 
1264711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: exit\n", __func__));
1265711bcba0SDavid C Somayajulu }
1266711bcba0SDavid C Somayajulu 
1267711bcba0SDavid C Somayajulu static void
1268711bcba0SDavid C Somayajulu qls_flush_xmt_bufs(qla_host_t *ha)
1269711bcba0SDavid C Somayajulu {
1270711bcba0SDavid C Somayajulu 	int		i, j;
1271711bcba0SDavid C Somayajulu 
1272711bcba0SDavid C Somayajulu 	for (j = 0; j < ha->num_tx_rings; j++) {
1273711bcba0SDavid C Somayajulu 		for (i = 0; i < NUM_TX_DESCRIPTORS; i++)
1274711bcba0SDavid C Somayajulu 			qls_flush_tx_buf(ha, &ha->tx_ring[j].tx_buf[i]);
1275711bcba0SDavid C Somayajulu 	}
1276711bcba0SDavid C Somayajulu 
1277711bcba0SDavid C Somayajulu 	return;
1278711bcba0SDavid C Somayajulu }
1279711bcba0SDavid C Somayajulu 
1280711bcba0SDavid C Somayajulu 
1281711bcba0SDavid C Somayajulu static int
1282711bcba0SDavid C Somayajulu qls_alloc_rcv_mbufs(qla_host_t *ha, int r)
1283711bcba0SDavid C Somayajulu {
1284711bcba0SDavid C Somayajulu 	int			i, j, ret = 0;
1285711bcba0SDavid C Somayajulu 	qla_rx_buf_t		*rxb;
1286711bcba0SDavid C Somayajulu 	qla_rx_ring_t		*rx_ring;
1287711bcba0SDavid C Somayajulu 	volatile q81_bq_addr_e_t *sbq_e;
1288711bcba0SDavid C Somayajulu 
1289711bcba0SDavid C Somayajulu 
1290711bcba0SDavid C Somayajulu 	rx_ring = &ha->rx_ring[r];
1291711bcba0SDavid C Somayajulu 
1292711bcba0SDavid C Somayajulu 	for (i = 0; i < NUM_RX_DESCRIPTORS; i++) {
1293711bcba0SDavid C Somayajulu 
1294711bcba0SDavid C Somayajulu 		rxb = &rx_ring->rx_buf[i];
1295711bcba0SDavid C Somayajulu 
1296711bcba0SDavid C Somayajulu 		ret = bus_dmamap_create(ha->rx_tag, BUS_DMA_NOWAIT, &rxb->map);
1297711bcba0SDavid C Somayajulu 
1298711bcba0SDavid C Somayajulu 		if (ret) {
1299711bcba0SDavid C Somayajulu 			device_printf(ha->pci_dev,
1300711bcba0SDavid C Somayajulu 				"%s: dmamap[%d, %d] failed\n", __func__, r, i);
1301711bcba0SDavid C Somayajulu 
1302711bcba0SDavid C Somayajulu 			for (j = 0; j < i; j++) {
1303711bcba0SDavid C Somayajulu 				rxb = &rx_ring->rx_buf[j];
1304711bcba0SDavid C Somayajulu 				bus_dmamap_destroy(ha->rx_tag, rxb->map);
1305711bcba0SDavid C Somayajulu 			}
1306711bcba0SDavid C Somayajulu 			goto qls_alloc_rcv_mbufs_err;
1307711bcba0SDavid C Somayajulu 		}
1308711bcba0SDavid C Somayajulu 	}
1309711bcba0SDavid C Somayajulu 
1310711bcba0SDavid C Somayajulu 	rx_ring = &ha->rx_ring[r];
1311711bcba0SDavid C Somayajulu 
1312711bcba0SDavid C Somayajulu 	sbq_e = rx_ring->sbq_vaddr;
1313711bcba0SDavid C Somayajulu 
1314711bcba0SDavid C Somayajulu 	rxb = &rx_ring->rx_buf[0];
1315711bcba0SDavid C Somayajulu 
1316711bcba0SDavid C Somayajulu 	for (i = 0; i < NUM_RX_DESCRIPTORS; i++) {
1317711bcba0SDavid C Somayajulu 
1318711bcba0SDavid C Somayajulu 		if (!(ret = qls_get_mbuf(ha, rxb, NULL))) {
1319711bcba0SDavid C Somayajulu 
1320711bcba0SDavid C Somayajulu 			/*
1321711bcba0SDavid C Somayajulu 		 	 * set the physical address in the
1322711bcba0SDavid C Somayajulu 			 * corresponding descriptor entry in the
1323711bcba0SDavid C Somayajulu 			 * receive ring/queue for the hba
1324711bcba0SDavid C Somayajulu 			 */
1325711bcba0SDavid C Somayajulu 
1326711bcba0SDavid C Somayajulu 			sbq_e->addr_lo = rxb->paddr & 0xFFFFFFFF;
1327711bcba0SDavid C Somayajulu 			sbq_e->addr_hi = (rxb->paddr >> 32) & 0xFFFFFFFF;
1328711bcba0SDavid C Somayajulu 
1329711bcba0SDavid C Somayajulu 		} else {
1330711bcba0SDavid C Somayajulu 			device_printf(ha->pci_dev,
1331711bcba0SDavid C Somayajulu 				"%s: qls_get_mbuf [%d, %d] failed\n",
1332711bcba0SDavid C Somayajulu 					__func__, r, i);
1333711bcba0SDavid C Somayajulu 			bus_dmamap_destroy(ha->rx_tag, rxb->map);
1334711bcba0SDavid C Somayajulu 			goto qls_alloc_rcv_mbufs_err;
1335711bcba0SDavid C Somayajulu 		}
1336711bcba0SDavid C Somayajulu 
1337711bcba0SDavid C Somayajulu 		rxb++;
1338711bcba0SDavid C Somayajulu 		sbq_e++;
1339711bcba0SDavid C Somayajulu 	}
1340711bcba0SDavid C Somayajulu 	return 0;
1341711bcba0SDavid C Somayajulu 
1342711bcba0SDavid C Somayajulu qls_alloc_rcv_mbufs_err:
1343711bcba0SDavid C Somayajulu 	return (-1);
1344711bcba0SDavid C Somayajulu }
1345711bcba0SDavid C Somayajulu 
1346711bcba0SDavid C Somayajulu static void
1347711bcba0SDavid C Somayajulu qls_free_rcv_bufs(qla_host_t *ha)
1348711bcba0SDavid C Somayajulu {
1349711bcba0SDavid C Somayajulu 	int		i, r;
1350711bcba0SDavid C Somayajulu 	qla_rx_buf_t	*rxb;
1351711bcba0SDavid C Somayajulu 	qla_rx_ring_t	*rxr;
1352711bcba0SDavid C Somayajulu 
1353711bcba0SDavid C Somayajulu 	for (r = 0; r < ha->num_rx_rings; r++) {
1354711bcba0SDavid C Somayajulu 
1355711bcba0SDavid C Somayajulu 		rxr = &ha->rx_ring[r];
1356711bcba0SDavid C Somayajulu 
1357711bcba0SDavid C Somayajulu 		for (i = 0; i < NUM_RX_DESCRIPTORS; i++) {
1358711bcba0SDavid C Somayajulu 
1359711bcba0SDavid C Somayajulu 			rxb = &rxr->rx_buf[i];
1360711bcba0SDavid C Somayajulu 
1361711bcba0SDavid C Somayajulu 			if (rxb->m_head != NULL) {
1362711bcba0SDavid C Somayajulu 				bus_dmamap_unload(ha->rx_tag, rxb->map);
1363711bcba0SDavid C Somayajulu 				bus_dmamap_destroy(ha->rx_tag, rxb->map);
1364711bcba0SDavid C Somayajulu 				m_freem(rxb->m_head);
1365711bcba0SDavid C Somayajulu 			}
1366711bcba0SDavid C Somayajulu 		}
1367711bcba0SDavid C Somayajulu 		bzero(rxr->rx_buf, (sizeof(qla_rx_buf_t) * NUM_RX_DESCRIPTORS));
1368711bcba0SDavid C Somayajulu 	}
1369711bcba0SDavid C Somayajulu 	return;
1370711bcba0SDavid C Somayajulu }
1371711bcba0SDavid C Somayajulu 
1372711bcba0SDavid C Somayajulu static int
1373711bcba0SDavid C Somayajulu qls_alloc_rcv_bufs(qla_host_t *ha)
1374711bcba0SDavid C Somayajulu {
1375711bcba0SDavid C Somayajulu 	int		r, ret = 0;
1376711bcba0SDavid C Somayajulu 	qla_rx_ring_t	*rxr;
1377711bcba0SDavid C Somayajulu 
1378711bcba0SDavid C Somayajulu 	for (r = 0; r < ha->num_rx_rings; r++) {
1379711bcba0SDavid C Somayajulu 		rxr = &ha->rx_ring[r];
1380711bcba0SDavid C Somayajulu 		bzero(rxr->rx_buf, (sizeof(qla_rx_buf_t) * NUM_RX_DESCRIPTORS));
1381711bcba0SDavid C Somayajulu 	}
1382711bcba0SDavid C Somayajulu 
1383711bcba0SDavid C Somayajulu 	for (r = 0; r < ha->num_rx_rings; r++) {
1384711bcba0SDavid C Somayajulu 
1385711bcba0SDavid C Somayajulu 		ret = qls_alloc_rcv_mbufs(ha, r);
1386711bcba0SDavid C Somayajulu 
1387711bcba0SDavid C Somayajulu 		if (ret)
1388711bcba0SDavid C Somayajulu 			qls_free_rcv_bufs(ha);
1389711bcba0SDavid C Somayajulu 	}
1390711bcba0SDavid C Somayajulu 
1391711bcba0SDavid C Somayajulu 	return (ret);
1392711bcba0SDavid C Somayajulu }
1393711bcba0SDavid C Somayajulu 
1394711bcba0SDavid C Somayajulu int
1395711bcba0SDavid C Somayajulu qls_get_mbuf(qla_host_t *ha, qla_rx_buf_t *rxb, struct mbuf *nmp)
1396711bcba0SDavid C Somayajulu {
13973e85b721SEd Maste 	struct mbuf *mp = nmp;
1398711bcba0SDavid C Somayajulu 	struct ifnet   		*ifp;
1399711bcba0SDavid C Somayajulu 	int            		ret = 0;
1400711bcba0SDavid C Somayajulu 	uint32_t		offset;
1401711bcba0SDavid C Somayajulu 	bus_dma_segment_t	segs[1];
1402711bcba0SDavid C Somayajulu 	int			nsegs;
1403711bcba0SDavid C Somayajulu 
1404711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: enter\n", __func__));
1405711bcba0SDavid C Somayajulu 
1406711bcba0SDavid C Somayajulu 	ifp = ha->ifp;
1407711bcba0SDavid C Somayajulu 
1408711bcba0SDavid C Somayajulu 	if (mp == NULL) {
1409711bcba0SDavid C Somayajulu 
1410b8c83a19SGleb Smirnoff 		mp = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, ha->msize);
1411711bcba0SDavid C Somayajulu 
1412711bcba0SDavid C Somayajulu 		if (mp == NULL) {
1413711bcba0SDavid C Somayajulu 
1414711bcba0SDavid C Somayajulu 			if (ha->msize == MCLBYTES)
1415711bcba0SDavid C Somayajulu 				ha->err_m_getcl++;
1416711bcba0SDavid C Somayajulu 			else
1417711bcba0SDavid C Somayajulu 				ha->err_m_getjcl++;
1418711bcba0SDavid C Somayajulu 
1419711bcba0SDavid C Somayajulu 			ret = ENOBUFS;
1420711bcba0SDavid C Somayajulu 			device_printf(ha->pci_dev,
1421711bcba0SDavid C Somayajulu 					"%s: m_getcl failed\n", __func__);
1422711bcba0SDavid C Somayajulu 			goto exit_qls_get_mbuf;
1423711bcba0SDavid C Somayajulu 		}
1424711bcba0SDavid C Somayajulu 		mp->m_len = mp->m_pkthdr.len = ha->msize;
1425711bcba0SDavid C Somayajulu 	} else {
1426711bcba0SDavid C Somayajulu 		mp->m_len = mp->m_pkthdr.len = ha->msize;
1427711bcba0SDavid C Somayajulu 		mp->m_data = mp->m_ext.ext_buf;
1428711bcba0SDavid C Somayajulu 		mp->m_next = NULL;
1429711bcba0SDavid C Somayajulu 	}
1430711bcba0SDavid C Somayajulu 
1431711bcba0SDavid C Somayajulu 	/* align the receive buffers to 8 byte boundary */
1432711bcba0SDavid C Somayajulu 	offset = (uint32_t)((unsigned long long)mp->m_data & 0x7ULL);
1433711bcba0SDavid C Somayajulu 	if (offset) {
1434711bcba0SDavid C Somayajulu 		offset = 8 - offset;
1435711bcba0SDavid C Somayajulu 		m_adj(mp, offset);
1436711bcba0SDavid C Somayajulu 	}
1437711bcba0SDavid C Somayajulu 
1438711bcba0SDavid C Somayajulu 	/*
1439711bcba0SDavid C Somayajulu 	 * Using memory from the mbuf cluster pool, invoke the bus_dma
1440711bcba0SDavid C Somayajulu 	 * machinery to arrange the memory mapping.
1441711bcba0SDavid C Somayajulu 	 */
1442711bcba0SDavid C Somayajulu 	ret = bus_dmamap_load_mbuf_sg(ha->rx_tag, rxb->map,
1443711bcba0SDavid C Somayajulu 			mp, segs, &nsegs, BUS_DMA_NOWAIT);
1444711bcba0SDavid C Somayajulu 	rxb->paddr = segs[0].ds_addr;
1445711bcba0SDavid C Somayajulu 
1446711bcba0SDavid C Somayajulu 	if (ret || !rxb->paddr || (nsegs != 1)) {
1447711bcba0SDavid C Somayajulu 		m_freem(mp);
1448711bcba0SDavid C Somayajulu 		rxb->m_head = NULL;
1449711bcba0SDavid C Somayajulu 		device_printf(ha->pci_dev,
1450711bcba0SDavid C Somayajulu 			"%s: bus_dmamap_load failed[%d, 0x%016llx, %d]\n",
1451711bcba0SDavid C Somayajulu 			__func__, ret, (long long unsigned int)rxb->paddr,
1452711bcba0SDavid C Somayajulu 			nsegs);
1453711bcba0SDavid C Somayajulu                 ret = -1;
1454711bcba0SDavid C Somayajulu 		goto exit_qls_get_mbuf;
1455711bcba0SDavid C Somayajulu 	}
1456711bcba0SDavid C Somayajulu 	rxb->m_head = mp;
1457711bcba0SDavid C Somayajulu 	bus_dmamap_sync(ha->rx_tag, rxb->map, BUS_DMASYNC_PREREAD);
1458711bcba0SDavid C Somayajulu 
1459711bcba0SDavid C Somayajulu exit_qls_get_mbuf:
1460711bcba0SDavid C Somayajulu 	QL_DPRINT2((ha->pci_dev, "%s: exit ret = 0x%08x\n", __func__, ret));
1461711bcba0SDavid C Somayajulu 	return (ret);
1462711bcba0SDavid C Somayajulu }
1463711bcba0SDavid C Somayajulu 
1464711bcba0SDavid C Somayajulu static void
1465711bcba0SDavid C Somayajulu qls_tx_done(void *context, int pending)
1466711bcba0SDavid C Somayajulu {
1467711bcba0SDavid C Somayajulu 	qla_host_t *ha = context;
1468711bcba0SDavid C Somayajulu 	struct ifnet   *ifp;
1469711bcba0SDavid C Somayajulu 
1470711bcba0SDavid C Somayajulu 	ifp = ha->ifp;
1471711bcba0SDavid C Somayajulu 
1472711bcba0SDavid C Somayajulu 	if (!ifp)
1473711bcba0SDavid C Somayajulu 		return;
1474711bcba0SDavid C Somayajulu 
1475711bcba0SDavid C Somayajulu 	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1476711bcba0SDavid C Somayajulu 		QL_DPRINT8((ha->pci_dev, "%s: !IFF_DRV_RUNNING\n", __func__));
1477711bcba0SDavid C Somayajulu 		return;
1478711bcba0SDavid C Somayajulu 	}
1479711bcba0SDavid C Somayajulu 
1480711bcba0SDavid C Somayajulu 	qls_start(ha->ifp);
1481711bcba0SDavid C Somayajulu 	return;
1482711bcba0SDavid C Somayajulu }
1483711bcba0SDavid C Somayajulu 
1484711bcba0SDavid C Somayajulu static int
1485711bcba0SDavid C Somayajulu qls_config_lro(qla_host_t *ha)
1486711bcba0SDavid C Somayajulu {
1487711bcba0SDavid C Somayajulu         int i;
1488711bcba0SDavid C Somayajulu         struct lro_ctrl *lro;
1489711bcba0SDavid C Somayajulu 
1490711bcba0SDavid C Somayajulu         for (i = 0; i < ha->num_rx_rings; i++) {
1491711bcba0SDavid C Somayajulu                 lro = &ha->rx_ring[i].lro;
1492711bcba0SDavid C Somayajulu                 if (tcp_lro_init(lro)) {
1493711bcba0SDavid C Somayajulu                         device_printf(ha->pci_dev, "%s: tcp_lro_init failed\n",
1494711bcba0SDavid C Somayajulu                                 __func__);
1495711bcba0SDavid C Somayajulu                         return (-1);
1496711bcba0SDavid C Somayajulu                 }
1497711bcba0SDavid C Somayajulu                 lro->ifp = ha->ifp;
1498711bcba0SDavid C Somayajulu         }
1499711bcba0SDavid C Somayajulu         ha->flags.lro_init = 1;
1500711bcba0SDavid C Somayajulu 
1501711bcba0SDavid C Somayajulu         QL_DPRINT2((ha->pci_dev, "%s: LRO initialized\n", __func__));
1502711bcba0SDavid C Somayajulu         return (0);
1503711bcba0SDavid C Somayajulu }
1504711bcba0SDavid C Somayajulu 
1505711bcba0SDavid C Somayajulu static void
1506711bcba0SDavid C Somayajulu qls_free_lro(qla_host_t *ha)
1507711bcba0SDavid C Somayajulu {
1508711bcba0SDavid C Somayajulu         int i;
1509711bcba0SDavid C Somayajulu         struct lro_ctrl *lro;
1510711bcba0SDavid C Somayajulu 
1511711bcba0SDavid C Somayajulu         if (!ha->flags.lro_init)
1512711bcba0SDavid C Somayajulu                 return;
1513711bcba0SDavid C Somayajulu 
1514711bcba0SDavid C Somayajulu         for (i = 0; i < ha->num_rx_rings; i++) {
1515711bcba0SDavid C Somayajulu                 lro = &ha->rx_ring[i].lro;
1516711bcba0SDavid C Somayajulu                 tcp_lro_free(lro);
1517711bcba0SDavid C Somayajulu         }
1518711bcba0SDavid C Somayajulu         ha->flags.lro_init = 0;
1519711bcba0SDavid C Somayajulu }
1520711bcba0SDavid C Somayajulu 
1521711bcba0SDavid C Somayajulu static void
1522711bcba0SDavid C Somayajulu qls_error_recovery(void *context, int pending)
1523711bcba0SDavid C Somayajulu {
1524711bcba0SDavid C Somayajulu         qla_host_t *ha = context;
1525711bcba0SDavid C Somayajulu 
1526711bcba0SDavid C Somayajulu 	qls_init(ha);
1527711bcba0SDavid C Somayajulu 
1528711bcba0SDavid C Somayajulu 	return;
1529711bcba0SDavid C Somayajulu }
1530711bcba0SDavid C Somayajulu 
1531