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