1227064Sbz/*
2227064Sbz * Copyright (c) 2010-2011 Qlogic Corporation
3227064Sbz * All rights reserved.
4227064Sbz *
5227064Sbz *  Redistribution and use in source and binary forms, with or without
6227064Sbz *  modification, are permitted provided that the following conditions
7227064Sbz *  are met:
8227064Sbz *
9227064Sbz *  1. Redistributions of source code must retain the above copyright
10227064Sbz *     notice, this list of conditions and the following disclaimer.
11227064Sbz *  2. Redistributions in binary form must reproduce the above copyright
12227064Sbz *     notice, this list of conditions and the following disclaimer in the
13227064Sbz *     documentation and/or other materials provided with the distribution.
14227064Sbz *
15227064Sbz *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16227064Sbz *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17227064Sbz *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18227064Sbz *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19227064Sbz *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20227064Sbz *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21227064Sbz *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22227064Sbz *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23227064Sbz *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24227064Sbz *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25227064Sbz *  POSSIBILITY OF SUCH DAMAGE.
26227064Sbz */
27227064Sbz
28227064Sbz/*
29227064Sbz * File: qla_hw.c
30227064Sbz * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656.
31227064Sbz * Content: Contains Hardware dependant functions
32227064Sbz */
33227064Sbz
34227064Sbz#include <sys/cdefs.h>
35227064Sbz__FBSDID("$FreeBSD$");
36227064Sbz
37227064Sbz#include "qla_os.h"
38227064Sbz#include "qla_reg.h"
39227064Sbz#include "qla_hw.h"
40227064Sbz#include "qla_def.h"
41227064Sbz#include "qla_inline.h"
42227064Sbz#include "qla_ver.h"
43227064Sbz#include "qla_glbl.h"
44227064Sbz#include "qla_dbg.h"
45227064Sbz
46227064Sbzstatic uint32_t sysctl_num_rds_rings = 2;
47227064Sbzstatic uint32_t sysctl_num_sds_rings = 4;
48227064Sbz
49227064Sbz/*
50227064Sbz * Static Functions
51227064Sbz */
52227064Sbz
53227064Sbzstatic void qla_init_cntxt_regions(qla_host_t *ha);
54227064Sbzstatic int qla_issue_cmd(qla_host_t *ha, qla_cdrp_t *cdrp);
55227064Sbzstatic int qla_fw_cmd(qla_host_t *ha, void *fw_cmd, uint32_t size);
56227064Sbzstatic int qla_config_mac_addr(qla_host_t *ha, uint8_t *mac_addr,
57227064Sbz		uint16_t cntxt_id, uint32_t add_multi);
58227064Sbzstatic void qla_del_rcv_cntxt(qla_host_t *ha);
59227064Sbzstatic int qla_init_rcv_cntxt(qla_host_t *ha);
60227064Sbzstatic void qla_del_xmt_cntxt(qla_host_t *ha);
61227064Sbzstatic int qla_init_xmt_cntxt(qla_host_t *ha);
62227064Sbzstatic int qla_get_max_rds(qla_host_t *ha);
63227064Sbzstatic int qla_get_max_sds(qla_host_t *ha);
64227064Sbzstatic int qla_get_max_rules(qla_host_t *ha);
65227064Sbzstatic int qla_get_max_rcv_cntxts(qla_host_t *ha);
66227064Sbzstatic int qla_get_max_tx_cntxts(qla_host_t *ha);
67227064Sbzstatic int qla_get_max_mtu(qla_host_t *ha);
68227064Sbzstatic int qla_get_max_lro(qla_host_t *ha);
69227064Sbzstatic int qla_get_flow_control(qla_host_t *ha);
70227064Sbzstatic void qla_hw_tx_done_locked(qla_host_t *ha);
71227064Sbz
72227064Sbzint
73227064Sbzqla_get_msix_count(qla_host_t *ha)
74227064Sbz{
75227064Sbz	return (sysctl_num_sds_rings);
76227064Sbz}
77227064Sbz
78227064Sbz/*
79227064Sbz * Name: qla_hw_add_sysctls
80227064Sbz * Function: Add P3Plus specific sysctls
81227064Sbz */
82227064Sbzvoid
83227064Sbzqla_hw_add_sysctls(qla_host_t *ha)
84227064Sbz{
85227064Sbz        device_t	dev;
86227064Sbz
87227064Sbz        dev = ha->pci_dev;
88227064Sbz
89227064Sbz        SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
90227064Sbz                SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
91227064Sbz                OID_AUTO, "num_rds_rings", CTLFLAG_RD, &sysctl_num_rds_rings,
92227064Sbz		sysctl_num_rds_rings, "Number of Rcv Descriptor Rings");
93227064Sbz
94227064Sbz        SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
95227064Sbz                SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
96227064Sbz                OID_AUTO, "num_sds_rings", CTLFLAG_RD, &sysctl_num_sds_rings,
97227064Sbz		sysctl_num_sds_rings, "Number of Status Descriptor Rings");
98227064Sbz}
99227064Sbz
100227064Sbz/*
101227064Sbz * Name: qla_free_dma
102227064Sbz * Function: Frees the DMA'able memory allocated in qla_alloc_dma()
103227064Sbz */
104227064Sbzvoid
105227064Sbzqla_free_dma(qla_host_t *ha)
106227064Sbz{
107227064Sbz	uint32_t i;
108227064Sbz
109227064Sbz        if (ha->hw.dma_buf.flags.context) {
110227064Sbz		qla_free_dmabuf(ha, &ha->hw.dma_buf.context);
111227064Sbz        	ha->hw.dma_buf.flags.context = 0;
112227064Sbz	}
113227064Sbz
114227064Sbz        if (ha->hw.dma_buf.flags.sds_ring) {
115227064Sbz		for (i = 0; i < ha->hw.num_sds_rings; i++)
116227064Sbz			qla_free_dmabuf(ha, &ha->hw.dma_buf.sds_ring[i]);
117227064Sbz        	ha->hw.dma_buf.flags.sds_ring = 0;
118227064Sbz	}
119227064Sbz
120227064Sbz        if (ha->hw.dma_buf.flags.rds_ring) {
121227064Sbz		for (i = 0; i < ha->hw.num_rds_rings; i++)
122227064Sbz			qla_free_dmabuf(ha, &ha->hw.dma_buf.rds_ring[i]);
123227064Sbz        	ha->hw.dma_buf.flags.rds_ring = 0;
124227064Sbz	}
125227064Sbz
126227064Sbz        if (ha->hw.dma_buf.flags.tx_ring) {
127227064Sbz		qla_free_dmabuf(ha, &ha->hw.dma_buf.tx_ring);
128227064Sbz        	ha->hw.dma_buf.flags.tx_ring = 0;
129227064Sbz	}
130227064Sbz}
131227064Sbz
132227064Sbz/*
133227064Sbz * Name: qla_alloc_dma
134227064Sbz * Function: Allocates DMA'able memory for Tx/Rx Rings, Tx/Rx Contexts.
135227064Sbz */
136227064Sbzint
137227064Sbzqla_alloc_dma(qla_host_t *ha)
138227064Sbz{
139227064Sbz        device_t                dev;
140227064Sbz	uint32_t		i, j, size;
141227064Sbz
142227064Sbz        dev = ha->pci_dev;
143227064Sbz
144227064Sbz        QL_DPRINT2((dev, "%s: enter\n", __func__));
145227064Sbz
146227064Sbz	ha->hw.num_rds_rings = (uint16_t)sysctl_num_rds_rings;
147227064Sbz	ha->hw.num_sds_rings = (uint16_t)sysctl_num_sds_rings;
148227064Sbz
149227064Sbz	/*
150227064Sbz	 * Allocate Transmit Ring
151227064Sbz	 */
152227064Sbz
153227064Sbz	ha->hw.dma_buf.tx_ring.alignment = 8;
154227064Sbz	ha->hw.dma_buf.tx_ring.size =
155227064Sbz		(sizeof(q80_tx_cmd_t)) * NUM_TX_DESCRIPTORS;
156227064Sbz
157227064Sbz        if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.tx_ring)) {
158227064Sbz                device_printf(dev, "%s: tx ring alloc failed\n", __func__);
159227064Sbz                goto qla_alloc_dma_exit;
160227064Sbz        }
161227064Sbz        ha->hw.dma_buf.flags.tx_ring = 1;
162227064Sbz
163227064Sbz	QL_DPRINT2((dev, "%s: tx_ring phys %p virt %p\n",
164227064Sbz		__func__, (void *)(ha->hw.dma_buf.tx_ring.dma_addr),
165227064Sbz		ha->hw.dma_buf.tx_ring.dma_b));
166227064Sbz	/*
167227064Sbz	 * Allocate Receive Descriptor Rings
168227064Sbz	 */
169227064Sbz
170227064Sbz	for (i = 0; i < ha->hw.num_rds_rings; i++) {
171227064Sbz		ha->hw.dma_buf.rds_ring[i].alignment = 8;
172227064Sbz
173227064Sbz		if (i == RDS_RING_INDEX_NORMAL) {
174227064Sbz			ha->hw.dma_buf.rds_ring[i].size =
175227064Sbz				(sizeof(q80_recv_desc_t)) * NUM_RX_DESCRIPTORS;
176227064Sbz		} else if (i == RDS_RING_INDEX_JUMBO) {
177227064Sbz			ha->hw.dma_buf.rds_ring[i].size =
178227064Sbz				(sizeof(q80_recv_desc_t)) *
179227064Sbz					NUM_RX_JUMBO_DESCRIPTORS;
180227064Sbz		} else
181227064Sbz			break;
182227064Sbz
183227064Sbz		if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.rds_ring[i])) {
184227064Sbz			QL_DPRINT4((dev, "%s: rds ring alloc failed\n",
185227064Sbz				__func__));
186227064Sbz
187227064Sbz			for (j = 0; j < i; j++)
188227064Sbz				qla_free_dmabuf(ha,
189227064Sbz					&ha->hw.dma_buf.rds_ring[j]);
190227064Sbz
191227064Sbz			goto qla_alloc_dma_exit;
192227064Sbz		}
193227064Sbz		QL_DPRINT4((dev, "%s: rx_ring[%d] phys %p virt %p\n",
194227064Sbz			__func__, i,
195227064Sbz			(void *)(ha->hw.dma_buf.rds_ring[i].dma_addr),
196227064Sbz			ha->hw.dma_buf.rds_ring[i].dma_b));
197227064Sbz	}
198227064Sbz	ha->hw.dma_buf.flags.rds_ring = 1;
199227064Sbz
200227064Sbz	/*
201227064Sbz	 * Allocate Status Descriptor Rings
202227064Sbz	 */
203227064Sbz
204227064Sbz	for (i = 0; i < ha->hw.num_sds_rings; i++) {
205227064Sbz		ha->hw.dma_buf.sds_ring[i].alignment = 8;
206227064Sbz		ha->hw.dma_buf.sds_ring[i].size =
207227064Sbz			(sizeof(q80_stat_desc_t)) * NUM_STATUS_DESCRIPTORS;
208227064Sbz
209227064Sbz		if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.sds_ring[i])) {
210227064Sbz			device_printf(dev, "%s: sds ring alloc failed\n",
211227064Sbz				__func__);
212227064Sbz
213227064Sbz			for (j = 0; j < i; j++)
214227064Sbz				qla_free_dmabuf(ha,
215227064Sbz					&ha->hw.dma_buf.sds_ring[j]);
216227064Sbz
217227064Sbz			goto qla_alloc_dma_exit;
218227064Sbz		}
219227064Sbz		QL_DPRINT4((dev, "%s: sds_ring[%d] phys %p virt %p\n",
220227064Sbz			__func__, i,
221227064Sbz			(void *)(ha->hw.dma_buf.sds_ring[i].dma_addr),
222227064Sbz			ha->hw.dma_buf.sds_ring[i].dma_b));
223227064Sbz	}
224227064Sbz	ha->hw.dma_buf.flags.sds_ring = 1;
225227064Sbz
226227064Sbz	/*
227227064Sbz	 * Allocate Context Area
228227064Sbz	 */
229227064Sbz	size = QL_ALIGN((sizeof (q80_tx_cntxt_req_t)), QL_BUFFER_ALIGN);
230227064Sbz
231227064Sbz	size += QL_ALIGN((sizeof (q80_tx_cntxt_rsp_t)), QL_BUFFER_ALIGN);
232227064Sbz
233227064Sbz	size += QL_ALIGN((sizeof (q80_rcv_cntxt_req_t)), QL_BUFFER_ALIGN);
234227064Sbz
235227064Sbz	size += QL_ALIGN((sizeof (q80_rcv_cntxt_rsp_t)), QL_BUFFER_ALIGN);
236227064Sbz
237227064Sbz	size += sizeof (uint32_t); /* for tx consumer index */
238227064Sbz
239227064Sbz	size = QL_ALIGN(size, PAGE_SIZE);
240227064Sbz
241227064Sbz	ha->hw.dma_buf.context.alignment = 8;
242227064Sbz	ha->hw.dma_buf.context.size = size;
243227064Sbz
244227064Sbz        if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.context)) {
245227064Sbz                device_printf(dev, "%s: context alloc failed\n", __func__);
246227064Sbz                goto qla_alloc_dma_exit;
247227064Sbz        }
248227064Sbz        ha->hw.dma_buf.flags.context = 1;
249227064Sbz	QL_DPRINT2((dev, "%s: context phys %p virt %p\n",
250227064Sbz		__func__, (void *)(ha->hw.dma_buf.context.dma_addr),
251227064Sbz		ha->hw.dma_buf.context.dma_b));
252227064Sbz
253227064Sbz	qla_init_cntxt_regions(ha);
254227064Sbz
255227064Sbz	return 0;
256227064Sbz
257227064Sbzqla_alloc_dma_exit:
258227064Sbz	qla_free_dma(ha);
259227064Sbz	return -1;
260227064Sbz}
261227064Sbz
262227064Sbz/*
263227064Sbz * Name: qla_init_cntxt_regions
264227064Sbz * Function: Initializes Tx/Rx Contexts.
265227064Sbz */
266227064Sbzstatic void
267227064Sbzqla_init_cntxt_regions(qla_host_t *ha)
268227064Sbz{
269227064Sbz	qla_hw_t		*hw;
270227064Sbz	q80_tx_cntxt_req_t	*tx_cntxt_req;
271227064Sbz	q80_rcv_cntxt_req_t	*rx_cntxt_req;
272227064Sbz	bus_addr_t		phys_addr;
273227064Sbz	uint32_t		i;
274227064Sbz        device_t                dev;
275227064Sbz	uint32_t		size;
276227064Sbz
277227064Sbz        dev = ha->pci_dev;
278227064Sbz
279227064Sbz	hw = &ha->hw;
280227064Sbz
281227064Sbz	hw->tx_ring_base = hw->dma_buf.tx_ring.dma_b;
282227064Sbz
283227064Sbz	for (i = 0; i < ha->hw.num_sds_rings; i++)
284227064Sbz		hw->sds[i].sds_ring_base =
285227064Sbz			(q80_stat_desc_t *)hw->dma_buf.sds_ring[i].dma_b;
286227064Sbz
287227064Sbz
288227064Sbz	phys_addr = hw->dma_buf.context.dma_addr;
289227064Sbz
290227064Sbz	memset((void *)hw->dma_buf.context.dma_b, 0,
291227064Sbz		ha->hw.dma_buf.context.size);
292227064Sbz
293227064Sbz	hw->tx_cntxt_req	=
294227064Sbz		(q80_tx_cntxt_req_t *)hw->dma_buf.context.dma_b;
295227064Sbz	hw->tx_cntxt_req_paddr	= phys_addr;
296227064Sbz
297227064Sbz	size = QL_ALIGN((sizeof (q80_tx_cntxt_req_t)), QL_BUFFER_ALIGN);
298227064Sbz
299227064Sbz	hw->tx_cntxt_rsp	=
300227064Sbz		(q80_tx_cntxt_rsp_t *)((uint8_t *)hw->tx_cntxt_req + size);
301227064Sbz	hw->tx_cntxt_rsp_paddr	= hw->tx_cntxt_req_paddr + size;
302227064Sbz
303227064Sbz	size = QL_ALIGN((sizeof (q80_tx_cntxt_rsp_t)), QL_BUFFER_ALIGN);
304227064Sbz
305227064Sbz	hw->rx_cntxt_req =
306227064Sbz		(q80_rcv_cntxt_req_t *)((uint8_t *)hw->tx_cntxt_rsp + size);
307227064Sbz	hw->rx_cntxt_req_paddr = hw->tx_cntxt_rsp_paddr + size;
308227064Sbz
309227064Sbz	size = QL_ALIGN((sizeof (q80_rcv_cntxt_req_t)), QL_BUFFER_ALIGN);
310227064Sbz
311227064Sbz	hw->rx_cntxt_rsp =
312227064Sbz		(q80_rcv_cntxt_rsp_t *)((uint8_t *)hw->rx_cntxt_req + size);
313227064Sbz	hw->rx_cntxt_rsp_paddr = hw->rx_cntxt_req_paddr + size;
314227064Sbz
315227064Sbz	size = QL_ALIGN((sizeof (q80_rcv_cntxt_rsp_t)), QL_BUFFER_ALIGN);
316227064Sbz
317227064Sbz	hw->tx_cons = (uint32_t *)((uint8_t *)hw->rx_cntxt_rsp + size);
318227064Sbz	hw->tx_cons_paddr = hw->rx_cntxt_rsp_paddr + size;
319227064Sbz
320227064Sbz	/*
321227064Sbz	 * Initialize the Transmit Context Request so that we don't need to
322227064Sbz	 * do it everytime we need to create a context
323227064Sbz	 */
324227064Sbz	tx_cntxt_req = hw->tx_cntxt_req;
325227064Sbz
326227064Sbz	tx_cntxt_req->rsp_dma_addr = qla_host_to_le64(hw->tx_cntxt_rsp_paddr);
327227064Sbz
328227064Sbz	tx_cntxt_req->cmd_cons_dma_addr = qla_host_to_le64(hw->tx_cons_paddr);
329227064Sbz
330227064Sbz	tx_cntxt_req->caps[0] = qla_host_to_le32((CNTXT_CAP0_BASEFW |
331227064Sbz					CNTXT_CAP0_LEGACY_MN | CNTXT_CAP0_LSO));
332227064Sbz
333227064Sbz	tx_cntxt_req->intr_mode = qla_host_to_le32(CNTXT_INTR_MODE_SHARED);
334227064Sbz
335227064Sbz	tx_cntxt_req->phys_addr =
336227064Sbz		qla_host_to_le64(hw->dma_buf.tx_ring.dma_addr);
337227064Sbz
338227064Sbz	tx_cntxt_req->num_entries = qla_host_to_le32(NUM_TX_DESCRIPTORS);
339227064Sbz
340227064Sbz	/*
341227064Sbz	 * Initialize the Receive Context Request
342227064Sbz	 */
343227064Sbz
344227064Sbz	rx_cntxt_req = hw->rx_cntxt_req;
345227064Sbz
346227064Sbz	rx_cntxt_req->rx_req.rsp_dma_addr =
347227064Sbz		qla_host_to_le64(hw->rx_cntxt_rsp_paddr);
348227064Sbz
349227064Sbz	rx_cntxt_req->rx_req.caps[0] = qla_host_to_le32(CNTXT_CAP0_BASEFW |
350227064Sbz						CNTXT_CAP0_LEGACY_MN |
351227064Sbz						CNTXT_CAP0_JUMBO |
352227064Sbz						CNTXT_CAP0_LRO|
353227064Sbz						CNTXT_CAP0_HW_LRO);
354227064Sbz
355227064Sbz	rx_cntxt_req->rx_req.intr_mode =
356227064Sbz		qla_host_to_le32(CNTXT_INTR_MODE_SHARED);
357227064Sbz
358227064Sbz	rx_cntxt_req->rx_req.rds_intr_mode =
359227064Sbz		qla_host_to_le32(CNTXT_INTR_MODE_UNIQUE);
360227064Sbz
361227064Sbz	rx_cntxt_req->rx_req.rds_ring_offset = 0;
362227064Sbz	rx_cntxt_req->rx_req.sds_ring_offset = qla_host_to_le32(
363227064Sbz		(hw->num_rds_rings * sizeof(q80_rq_rds_ring_t)));
364227064Sbz	rx_cntxt_req->rx_req.num_rds_rings =
365227064Sbz		qla_host_to_le16(hw->num_rds_rings);
366227064Sbz	rx_cntxt_req->rx_req.num_sds_rings =
367227064Sbz		qla_host_to_le16(hw->num_sds_rings);
368227064Sbz
369227064Sbz	for (i = 0; i < hw->num_rds_rings; i++) {
370227064Sbz		rx_cntxt_req->rds_req[i].phys_addr =
371227064Sbz			qla_host_to_le64(hw->dma_buf.rds_ring[i].dma_addr);
372227064Sbz
373227064Sbz		if (i == RDS_RING_INDEX_NORMAL) {
374227064Sbz			rx_cntxt_req->rds_req[i].buf_size =
375227064Sbz				qla_host_to_le64(MCLBYTES);
376227064Sbz			rx_cntxt_req->rds_req[i].size =
377227064Sbz				qla_host_to_le32(NUM_RX_DESCRIPTORS);
378227064Sbz		} else {
379227064Sbz			rx_cntxt_req->rds_req[i].buf_size =
380227064Sbz				qla_host_to_le64(MJUM9BYTES);
381227064Sbz			rx_cntxt_req->rds_req[i].size =
382227064Sbz				qla_host_to_le32(NUM_RX_JUMBO_DESCRIPTORS);
383227064Sbz		}
384227064Sbz	}
385227064Sbz
386227064Sbz	for (i = 0; i < hw->num_sds_rings; i++) {
387227064Sbz		rx_cntxt_req->sds_req[i].phys_addr =
388227064Sbz			qla_host_to_le64(hw->dma_buf.sds_ring[i].dma_addr);
389227064Sbz		rx_cntxt_req->sds_req[i].size =
390227064Sbz			qla_host_to_le32(NUM_STATUS_DESCRIPTORS);
391227064Sbz		rx_cntxt_req->sds_req[i].msi_index = qla_host_to_le16(i);
392227064Sbz	}
393227064Sbz
394227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: tx_cntxt_req = %p paddr %p\n",
395227064Sbz		__func__, hw->tx_cntxt_req, (void *)hw->tx_cntxt_req_paddr));
396227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: tx_cntxt_rsp = %p paddr %p\n",
397227064Sbz		__func__, hw->tx_cntxt_rsp, (void *)hw->tx_cntxt_rsp_paddr));
398227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: rx_cntxt_req = %p paddr %p\n",
399227064Sbz		__func__, hw->rx_cntxt_req, (void *)hw->rx_cntxt_req_paddr));
400227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: rx_cntxt_rsp = %p paddr %p\n",
401227064Sbz		__func__, hw->rx_cntxt_rsp, (void *)hw->rx_cntxt_rsp_paddr));
402227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: tx_cons      = %p paddr %p\n",
403227064Sbz		__func__, hw->tx_cons, (void *)hw->tx_cons_paddr));
404227064Sbz}
405227064Sbz
406227064Sbz/*
407227064Sbz * Name: qla_issue_cmd
408227064Sbz * Function: Issues commands on the CDRP interface and returns responses.
409227064Sbz */
410227064Sbzstatic int
411227064Sbzqla_issue_cmd(qla_host_t *ha, qla_cdrp_t *cdrp)
412227064Sbz{
413227064Sbz	int	ret = 0;
414227064Sbz	uint32_t signature;
415227064Sbz	uint32_t count = 400; /* 4 seconds or 400 10ms intervals */
416227064Sbz	uint32_t data;
417227064Sbz	device_t dev;
418227064Sbz
419227064Sbz	dev = ha->pci_dev;
420227064Sbz
421227064Sbz	signature = 0xcafe0000 | 0x0100 | ha->pci_func;
422227064Sbz
423227064Sbz	ret = qla_sem_lock(ha, Q8_SEM5_LOCK, 0, (uint32_t)ha->pci_func);
424227064Sbz
425227064Sbz	if (ret) {
426227064Sbz		device_printf(dev, "%s: SEM5_LOCK lock failed\n", __func__);
427227064Sbz		return (ret);
428227064Sbz	}
429227064Sbz
430227064Sbz	WRITE_OFFSET32(ha, Q8_NX_CDRP_SIGNATURE, signature);
431227064Sbz
432227064Sbz	WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG1, (cdrp->cmd_arg1));
433227064Sbz	WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG2, (cdrp->cmd_arg2));
434227064Sbz	WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG3, (cdrp->cmd_arg3));
435227064Sbz
436227064Sbz	WRITE_OFFSET32(ha, Q8_NX_CDRP_CMD_RSP, cdrp->cmd);
437227064Sbz
438227064Sbz	while (count) {
439227064Sbz		qla_mdelay(__func__, 10);
440227064Sbz
441227064Sbz		data = READ_REG32(ha, Q8_NX_CDRP_CMD_RSP);
442227064Sbz
443227064Sbz		if ((!(data & 0x80000000)))
444227064Sbz			break;
445227064Sbz		count--;
446227064Sbz	}
447227064Sbz	if ((!count) || (data != 1))
448227064Sbz		ret = -1;
449227064Sbz
450227064Sbz	cdrp->rsp = READ_REG32(ha, Q8_NX_CDRP_CMD_RSP);
451227064Sbz	cdrp->rsp_arg1 = READ_REG32(ha, Q8_NX_CDRP_ARG1);
452227064Sbz	cdrp->rsp_arg2 = READ_REG32(ha, Q8_NX_CDRP_ARG2);
453227064Sbz	cdrp->rsp_arg3 = READ_REG32(ha, Q8_NX_CDRP_ARG3);
454227064Sbz
455227064Sbz	qla_sem_unlock(ha, Q8_SEM5_UNLOCK);
456227064Sbz
457227064Sbz	if (ret) {
458227064Sbz		device_printf(dev, "%s: "
459227064Sbz			"cmd[0x%08x] = 0x%08x\n"
460227064Sbz			"\tsig[0x%08x] = 0x%08x\n"
461227064Sbz			"\targ1[0x%08x] = 0x%08x\n"
462227064Sbz			"\targ2[0x%08x] = 0x%08x\n"
463227064Sbz			"\targ3[0x%08x] = 0x%08x\n",
464227064Sbz			__func__, Q8_NX_CDRP_CMD_RSP, cdrp->cmd,
465227064Sbz			Q8_NX_CDRP_SIGNATURE, signature,
466227064Sbz			Q8_NX_CDRP_ARG1, cdrp->cmd_arg1,
467227064Sbz			Q8_NX_CDRP_ARG2, cdrp->cmd_arg2,
468227064Sbz			Q8_NX_CDRP_ARG3, cdrp->cmd_arg3);
469227064Sbz
470227064Sbz		device_printf(dev, "%s: exit (ret = 0x%x)\n"
471227064Sbz			"\t\t rsp = 0x%08x\n"
472227064Sbz			"\t\t arg1 = 0x%08x\n"
473227064Sbz			"\t\t arg2 = 0x%08x\n"
474227064Sbz			"\t\t arg3 = 0x%08x\n",
475227064Sbz			__func__, ret, cdrp->rsp,
476227064Sbz			cdrp->rsp_arg1, cdrp->rsp_arg2, cdrp->rsp_arg3);
477227064Sbz	}
478227064Sbz
479227064Sbz	return (ret);
480227064Sbz}
481227064Sbz
482227064Sbz#define QLA_TX_MIN_FREE	2
483227064Sbz
484227064Sbz/*
485227064Sbz * Name: qla_fw_cmd
486227064Sbz * Function: Issues firmware control commands on the Tx Ring.
487227064Sbz */
488227064Sbzstatic int
489227064Sbzqla_fw_cmd(qla_host_t *ha, void *fw_cmd, uint32_t size)
490227064Sbz{
491227064Sbz	device_t dev;
492227064Sbz        q80_tx_cmd_t *tx_cmd;
493227064Sbz        qla_hw_t *hw = &ha->hw;
494227064Sbz	int count = 100;
495227064Sbz
496227064Sbz	dev = ha->pci_dev;
497227064Sbz
498227064Sbz	QLA_TX_LOCK(ha);
499227064Sbz
500227064Sbz        if (hw->txr_free <= QLA_TX_MIN_FREE) {
501227064Sbz		while (count--) {
502227064Sbz			qla_hw_tx_done_locked(ha);
503227064Sbz			if (hw->txr_free > QLA_TX_MIN_FREE)
504227064Sbz				break;
505227064Sbz
506227064Sbz			QLA_TX_UNLOCK(ha);
507227064Sbz			qla_mdelay(__func__, 10);
508227064Sbz			QLA_TX_LOCK(ha);
509227064Sbz		}
510227064Sbz        	if (hw->txr_free <= QLA_TX_MIN_FREE) {
511227064Sbz			QLA_TX_UNLOCK(ha);
512227064Sbz			device_printf(dev, "%s: xmit queue full\n", __func__);
513227064Sbz                	return (-1);
514227064Sbz		}
515227064Sbz        }
516227064Sbz        tx_cmd = &hw->tx_ring_base[hw->txr_next];
517227064Sbz
518227064Sbz        bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t));
519227064Sbz
520227064Sbz	bcopy(fw_cmd, tx_cmd, size);
521227064Sbz
522227064Sbz	hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1);
523227064Sbz	hw->txr_free--;
524227064Sbz
525227064Sbz	QL_UPDATE_TX_PRODUCER_INDEX(ha, hw->txr_next);
526227064Sbz
527227064Sbz	QLA_TX_UNLOCK(ha);
528227064Sbz
529227064Sbz	return (0);
530227064Sbz}
531227064Sbz
532227064Sbz/*
533227064Sbz * Name: qla_config_rss
534227064Sbz * Function: Configure RSS for the context/interface.
535227064Sbz */
536227064Sbzconst uint64_t rss_key[] = { 0xbeac01fa6a42b73bULL, 0x8030f20c77cb2da3ULL,
537227064Sbz			0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL,
538227064Sbz			0x255b0ec26d5a56daULL };
539227064Sbz
540227064Sbzstatic int
541227064Sbzqla_config_rss(qla_host_t *ha, uint16_t cntxt_id)
542227064Sbz{
543227064Sbz	qla_fw_cds_config_rss_t rss_config;
544227064Sbz	int ret, i;
545227064Sbz
546227064Sbz	bzero(&rss_config, sizeof(qla_fw_cds_config_rss_t));
547227064Sbz
548227064Sbz	rss_config.hdr.cmd = Q8_FWCD_CNTRL_REQ;
549227064Sbz	rss_config.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_RSS;
550227064Sbz	rss_config.hdr.cntxt_id = cntxt_id;
551227064Sbz
552227064Sbz	rss_config.hash_type = (Q8_FWCD_RSS_HASH_TYPE_IPV4_TCP_IP |
553227064Sbz					Q8_FWCD_RSS_HASH_TYPE_IPV6_TCP_IP);
554227064Sbz	rss_config.flags = Q8_FWCD_RSS_FLAGS_ENABLE_RSS;
555227064Sbz
556227064Sbz	rss_config.ind_tbl_mask = 0x7;
557227064Sbz
558227064Sbz	for (i = 0; i < 5; i++)
559227064Sbz		rss_config.rss_key[i] = rss_key[i];
560227064Sbz
561227064Sbz	ret = qla_fw_cmd(ha, &rss_config, sizeof(qla_fw_cds_config_rss_t));
562227064Sbz
563227064Sbz	return ret;
564227064Sbz}
565227064Sbz
566227064Sbz/*
567227064Sbz * Name: qla_config_intr_coalesce
568227064Sbz * Function: Configure Interrupt Coalescing.
569227064Sbz */
570227064Sbzstatic int
571227064Sbzqla_config_intr_coalesce(qla_host_t *ha, uint16_t cntxt_id, int tenable)
572227064Sbz{
573227064Sbz	qla_fw_cds_config_intr_coalesc_t intr_coalesce;
574227064Sbz	int ret;
575227064Sbz
576227064Sbz	bzero(&intr_coalesce, sizeof(qla_fw_cds_config_intr_coalesc_t));
577227064Sbz
578227064Sbz	intr_coalesce.hdr.cmd = Q8_FWCD_CNTRL_REQ;
579227064Sbz	intr_coalesce.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_INTR_COALESCING;
580227064Sbz	intr_coalesce.hdr.cntxt_id = cntxt_id;
581227064Sbz
582227064Sbz	intr_coalesce.flags = 0x04;
583227064Sbz	intr_coalesce.max_rcv_pkts = 256;
584227064Sbz	intr_coalesce.max_rcv_usecs = 3;
585227064Sbz	intr_coalesce.max_snd_pkts = 64;
586227064Sbz	intr_coalesce.max_snd_usecs = 4;
587227064Sbz
588227064Sbz	if (tenable) {
589227064Sbz		intr_coalesce.usecs_to = 1000; /* 1 millisecond */
590227064Sbz		intr_coalesce.timer_type = Q8_FWCMD_INTR_COALESC_TIMER_PERIODIC;
591227064Sbz		intr_coalesce.sds_ring_bitmask =
592227064Sbz			Q8_FWCMD_INTR_COALESC_SDS_RING_0;
593227064Sbz	}
594227064Sbz
595227064Sbz	ret = qla_fw_cmd(ha, &intr_coalesce,
596227064Sbz			sizeof(qla_fw_cds_config_intr_coalesc_t));
597227064Sbz
598227064Sbz	return ret;
599227064Sbz}
600227064Sbz
601227064Sbz
602227064Sbz/*
603227064Sbz * Name: qla_config_mac_addr
604227064Sbz * Function: binds a MAC address to the context/interface.
605227064Sbz *	Can be unicast, multicast or broadcast.
606227064Sbz */
607227064Sbzstatic int
608227064Sbzqla_config_mac_addr(qla_host_t *ha, uint8_t *mac_addr, uint16_t cntxt_id,
609227064Sbz	uint32_t add_multi)
610227064Sbz{
611227064Sbz	qla_fw_cds_config_mac_addr_t mac_config;
612227064Sbz	int ret;
613227064Sbz
614227064Sbz//	device_printf(ha->pci_dev,
615227064Sbz//		"%s: mac_addr %02x:%02x:%02x:%02x:%02x:%02x\n", __func__,
616227064Sbz//		mac_addr[0], mac_addr[1], mac_addr[2],
617227064Sbz//		mac_addr[3], mac_addr[4], mac_addr[5]);
618227064Sbz
619227064Sbz	bzero(&mac_config, sizeof(qla_fw_cds_config_mac_addr_t));
620227064Sbz
621227064Sbz	mac_config.hdr.cmd = Q8_FWCD_CNTRL_REQ;
622227064Sbz	mac_config.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_MAC_ADDR;
623227064Sbz	mac_config.hdr.cntxt_id = cntxt_id;
624227064Sbz
625227064Sbz	if (add_multi)
626227064Sbz		mac_config.cmd = Q8_FWCD_ADD_MAC_ADDR;
627227064Sbz	else
628227064Sbz		mac_config.cmd = Q8_FWCD_DEL_MAC_ADDR;
629227064Sbz	bcopy(mac_addr, mac_config.mac_addr,6);
630227064Sbz
631227064Sbz	ret = qla_fw_cmd(ha, &mac_config, sizeof(qla_fw_cds_config_mac_addr_t));
632227064Sbz
633227064Sbz	return ret;
634227064Sbz}
635227064Sbz
636227064Sbz
637227064Sbz/*
638227064Sbz * Name: qla_set_mac_rcv_mode
639227064Sbz * Function: Enable/Disable AllMulticast and Promiscous Modes.
640227064Sbz */
641227064Sbzstatic int
642227064Sbzqla_set_mac_rcv_mode(qla_host_t *ha, uint16_t cntxt_id, uint32_t mode)
643227064Sbz{
644227064Sbz	qla_set_mac_rcv_mode_t rcv_mode;
645227064Sbz	int ret;
646227064Sbz
647227064Sbz	bzero(&rcv_mode, sizeof(qla_set_mac_rcv_mode_t));
648227064Sbz
649227064Sbz	rcv_mode.hdr.cmd = Q8_FWCD_CNTRL_REQ;
650227064Sbz	rcv_mode.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_MAC_RCV_MODE;
651227064Sbz	rcv_mode.hdr.cntxt_id = cntxt_id;
652227064Sbz
653227064Sbz	rcv_mode.mode = mode;
654227064Sbz
655227064Sbz	ret = qla_fw_cmd(ha, &rcv_mode, sizeof(qla_set_mac_rcv_mode_t));
656227064Sbz
657227064Sbz	return ret;
658227064Sbz}
659227064Sbz
660227064Sbzvoid
661227064Sbzqla_set_promisc(qla_host_t *ha)
662227064Sbz{
663227064Sbz	(void)qla_set_mac_rcv_mode(ha,
664227064Sbz		(ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id,
665227064Sbz		Q8_MAC_RCV_ENABLE_PROMISCUOUS);
666227064Sbz}
667227064Sbz
668227064Sbzvoid
669227064Sbzqla_set_allmulti(qla_host_t *ha)
670227064Sbz{
671227064Sbz	(void)qla_set_mac_rcv_mode(ha,
672227064Sbz		(ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id,
673227064Sbz		Q8_MAC_RCV_ENABLE_ALLMULTI);
674227064Sbz}
675227064Sbz
676227064Sbzvoid
677227064Sbzqla_reset_promisc_allmulti(qla_host_t *ha)
678227064Sbz{
679227064Sbz	(void)qla_set_mac_rcv_mode(ha,
680227064Sbz		(ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id,
681227064Sbz		Q8_MAC_RCV_RESET_PROMISC_ALLMULTI);
682227064Sbz}
683227064Sbz
684227064Sbz/*
685227064Sbz * Name: qla_config_ipv4_addr
686227064Sbz * Function: Configures the Destination IP Addr for LRO.
687227064Sbz */
688227064Sbzvoid
689227064Sbzqla_config_ipv4_addr(qla_host_t *ha, uint32_t ipv4_addr)
690227064Sbz{
691227064Sbz	qla_config_ipv4_t ip_conf;
692227064Sbz
693227064Sbz	bzero(&ip_conf, sizeof(qla_config_ipv4_t));
694227064Sbz
695227064Sbz	ip_conf.hdr.cmd = Q8_FWCD_CNTRL_REQ;
696227064Sbz	ip_conf.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_IPADDR;
697227064Sbz	ip_conf.hdr.cntxt_id = (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id;
698227064Sbz
699227064Sbz	ip_conf.cmd = (uint64_t)Q8_CONFIG_CMD_IP_ENABLE;
700227064Sbz	ip_conf.ipv4_addr = (uint64_t)ipv4_addr;
701227064Sbz
702227064Sbz	(void)qla_fw_cmd(ha, &ip_conf, sizeof(qla_config_ipv4_t));
703227064Sbz
704227064Sbz	return;
705227064Sbz}
706227064Sbz
707227064Sbz/*
708227064Sbz * Name: qla_tx_tso
709227064Sbz * Function: Checks if the packet to be transmitted is a candidate for
710227064Sbz *	Large TCP Segment Offload. If yes, the appropriate fields in the Tx
711227064Sbz *	Ring Structure are plugged in.
712227064Sbz */
713227064Sbzstatic int
714227064Sbzqla_tx_tso(qla_host_t *ha, struct mbuf *mp, q80_tx_cmd_t *tx_cmd)
715227064Sbz{
716227064Sbz	struct ether_vlan_header *eh;
717227064Sbz	struct ip *ip = NULL;
718227064Sbz	struct tcphdr *th = NULL;
719227064Sbz	uint32_t ehdrlen,  hdrlen, ip_hlen, tcp_hlen;
720227064Sbz	uint16_t etype, opcode, offload = 1;
721227064Sbz	device_t dev;
722227064Sbz
723227064Sbz	dev = ha->pci_dev;
724227064Sbz
725227064Sbz	if (mp->m_pkthdr.len <= ha->max_frame_size)
726227064Sbz		return (-1);
727227064Sbz
728227064Sbz	eh = mtod(mp, struct ether_vlan_header *);
729227064Sbz
730227064Sbz	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
731227064Sbz		ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
732227064Sbz		etype = ntohs(eh->evl_proto);
733227064Sbz	} else {
734227064Sbz		ehdrlen = ETHER_HDR_LEN;
735227064Sbz		etype = ntohs(eh->evl_encap_proto);
736227064Sbz	}
737227064Sbz
738227064Sbz	switch (etype) {
739227064Sbz		case ETHERTYPE_IP:
740227064Sbz			ip = (struct ip *)(mp->m_data + ehdrlen);
741227064Sbz			ip_hlen = ip->ip_hl << 2;
742227064Sbz			opcode = Q8_TX_CMD_OP_XMT_TCP_LSO;
743227064Sbz
744227064Sbz			if (ip->ip_p != IPPROTO_TCP) {
745227064Sbz				offload = 0;
746227064Sbz			} else
747227064Sbz				th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
748227064Sbz		break;
749227064Sbz
750227064Sbz		default:
751227064Sbz			QL_DPRINT8((dev, "%s: type!=ip\n", __func__));
752227064Sbz			offload = 0;
753227064Sbz		break;
754227064Sbz	}
755227064Sbz
756227064Sbz	if (!offload)
757227064Sbz		return (-1);
758227064Sbz
759227064Sbz	tcp_hlen = th->th_off << 2;
760227064Sbz
761227064Sbz	hdrlen = ehdrlen + ip_hlen + tcp_hlen;
762227064Sbz
763227064Sbz	if (mp->m_len < hdrlen) {
764227064Sbz		device_printf(dev, "%s: (mp->m_len < hdrlen)\n", __func__);
765227064Sbz		return (-1);
766227064Sbz	}
767227064Sbz
768227064Sbz	tx_cmd->flags_opcode = opcode ;
769227064Sbz	tx_cmd->tcp_hdr_off = ip_hlen + ehdrlen;
770227064Sbz	tx_cmd->ip_hdr_off = ehdrlen;
771227064Sbz	tx_cmd->mss = mp->m_pkthdr.tso_segsz;
772227064Sbz	tx_cmd->total_hdr_len = hdrlen;
773227064Sbz
774227064Sbz	/* Check for Multicast least significant bit of MSB == 1 */
775227064Sbz	if (eh->evl_dhost[0] & 0x01) {
776227064Sbz		tx_cmd->flags_opcode = Q8_TX_CMD_FLAGS_MULTICAST;
777227064Sbz	}
778227064Sbz
779227064Sbz	return (0);
780227064Sbz}
781227064Sbz
782227064Sbz/*
783227064Sbz * Name: qla_tx_chksum
784227064Sbz * Function: Checks if the packet to be transmitted is a candidate for
785227064Sbz *	TCP/UDP Checksum offload. If yes, the appropriate fields in the Tx
786227064Sbz *	Ring Structure are plugged in.
787227064Sbz */
788227064Sbzstatic int
789227064Sbzqla_tx_chksum(qla_host_t *ha, struct mbuf *mp, q80_tx_cmd_t *tx_cmd)
790227064Sbz{
791227064Sbz	struct ether_vlan_header *eh;
792227064Sbz	struct ip *ip;
793227064Sbz	struct ip6_hdr *ip6;
794227064Sbz	uint32_t ehdrlen, ip_hlen;
795227064Sbz	uint16_t etype, opcode, offload = 1;
796227064Sbz	device_t dev;
797227064Sbz
798227064Sbz	dev = ha->pci_dev;
799227064Sbz
800227064Sbz	if ((mp->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) == 0)
801227064Sbz		return (-1);
802227064Sbz
803227064Sbz	eh = mtod(mp, struct ether_vlan_header *);
804227064Sbz
805227064Sbz	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
806227064Sbz		ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
807227064Sbz		etype = ntohs(eh->evl_proto);
808227064Sbz	} else {
809227064Sbz		ehdrlen = ETHER_HDR_LEN;
810227064Sbz		etype = ntohs(eh->evl_encap_proto);
811227064Sbz	}
812227064Sbz
813227064Sbz
814227064Sbz	switch (etype) {
815227064Sbz		case ETHERTYPE_IP:
816227064Sbz			ip = (struct ip *)(mp->m_data + ehdrlen);
817227064Sbz
818227064Sbz			ip_hlen = ip->ip_hl << 2;
819227064Sbz
820227064Sbz			if (mp->m_len < (ehdrlen + ip_hlen)) {
821227064Sbz				device_printf(dev, "%s: ipv4 mlen\n", __func__);
822227064Sbz				offload = 0;
823227064Sbz				break;
824227064Sbz			}
825227064Sbz
826227064Sbz			if (ip->ip_p == IPPROTO_TCP)
827227064Sbz				opcode = Q8_TX_CMD_OP_XMT_TCP_CHKSUM;
828227064Sbz			else if (ip->ip_p == IPPROTO_UDP)
829227064Sbz				opcode = Q8_TX_CMD_OP_XMT_UDP_CHKSUM;
830227064Sbz			else {
831227064Sbz				device_printf(dev, "%s: ipv4\n", __func__);
832227064Sbz				offload = 0;
833227064Sbz			}
834227064Sbz		break;
835227064Sbz
836227064Sbz		case ETHERTYPE_IPV6:
837227064Sbz			ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
838227064Sbz
839227064Sbz			ip_hlen = sizeof(struct ip6_hdr);
840227064Sbz
841227064Sbz			if (mp->m_len < (ehdrlen + ip_hlen)) {
842227064Sbz				device_printf(dev, "%s: ipv6 mlen\n", __func__);
843227064Sbz				offload = 0;
844227064Sbz				break;
845227064Sbz			}
846227064Sbz
847227064Sbz			if (ip6->ip6_nxt == IPPROTO_TCP)
848227064Sbz				opcode = Q8_TX_CMD_OP_XMT_TCP_CHKSUM_IPV6;
849227064Sbz			else if (ip6->ip6_nxt == IPPROTO_UDP)
850227064Sbz				opcode = Q8_TX_CMD_OP_XMT_UDP_CHKSUM_IPV6;
851227064Sbz			else {
852227064Sbz				device_printf(dev, "%s: ipv6\n", __func__);
853227064Sbz				offload = 0;
854227064Sbz			}
855227064Sbz		break;
856227064Sbz
857227064Sbz		default:
858227064Sbz			offload = 0;
859227064Sbz		break;
860227064Sbz	}
861227064Sbz	if (!offload)
862227064Sbz		return (-1);
863227064Sbz
864227064Sbz	tx_cmd->flags_opcode = opcode;
865227064Sbz
866227064Sbz	tx_cmd->tcp_hdr_off = ip_hlen + ehdrlen;
867227064Sbz
868227064Sbz	return (0);
869227064Sbz}
870227064Sbz
871227064Sbz/*
872227064Sbz * Name: qla_hw_send
873227064Sbz * Function: Transmits a packet. It first checks if the packet is a
874227064Sbz *	candidate for Large TCP Segment Offload and then for UDP/TCP checksum
875227064Sbz *	offload. If either of these creteria are not met, it is transmitted
876227064Sbz *	as a regular ethernet frame.
877227064Sbz */
878227064Sbzint
879227064Sbzqla_hw_send(qla_host_t *ha, bus_dma_segment_t *segs, int nsegs,
880227064Sbz	uint32_t *tx_idx,  struct mbuf *mp)
881227064Sbz{
882227064Sbz	struct ether_vlan_header *eh;
883227064Sbz	qla_hw_t *hw = &ha->hw;
884227064Sbz	q80_tx_cmd_t *tx_cmd, tso_cmd;
885227064Sbz	bus_dma_segment_t *c_seg;
886227064Sbz	uint32_t num_tx_cmds, hdr_len = 0;
887227064Sbz	uint32_t total_length = 0, bytes, tx_cmd_count = 0;
888227064Sbz	device_t dev;
889227064Sbz	int i;
890227064Sbz
891227064Sbz	dev = ha->pci_dev;
892227064Sbz
893227064Sbz	/*
894227064Sbz	 * Always make sure there is atleast one empty slot in the tx_ring
895227064Sbz	 * tx_ring is considered full when there only one entry available
896227064Sbz	 */
897227064Sbz        num_tx_cmds = (nsegs + (Q8_TX_CMD_MAX_SEGMENTS - 1)) >> 2;
898227064Sbz
899227064Sbz	total_length = mp->m_pkthdr.len;
900227064Sbz	if (total_length > QLA_MAX_TSO_FRAME_SIZE) {
901227064Sbz		device_printf(dev, "%s: total length exceeds maxlen(%d)\n",
902227064Sbz			__func__, total_length);
903227064Sbz		return (-1);
904227064Sbz	}
905227064Sbz
906227064Sbz	bzero((void *)&tso_cmd, sizeof(q80_tx_cmd_t));
907227064Sbz
908227064Sbz	if (qla_tx_tso(ha, mp, &tso_cmd) == 0) {
909227064Sbz		/* find the additional tx_cmd descriptors required */
910227064Sbz
911227064Sbz		hdr_len = tso_cmd.total_hdr_len;
912227064Sbz
913227064Sbz		bytes = sizeof(q80_tx_cmd_t) - Q8_TX_CMD_TSO_ALIGN;
914227064Sbz		bytes = QL_MIN(bytes, hdr_len);
915227064Sbz
916227064Sbz		num_tx_cmds++;
917227064Sbz		hdr_len -= bytes;
918227064Sbz
919227064Sbz		while (hdr_len) {
920227064Sbz			bytes = QL_MIN((sizeof(q80_tx_cmd_t)), hdr_len);
921227064Sbz			hdr_len -= bytes;
922227064Sbz			num_tx_cmds++;
923227064Sbz		}
924227064Sbz		hdr_len = tso_cmd.total_hdr_len;
925227064Sbz	}
926227064Sbz
927227064Sbz	if (hw->txr_free <= (num_tx_cmds + QLA_TX_MIN_FREE)) {
928227064Sbz		qla_hw_tx_done_locked(ha);
929227064Sbz		if (hw->txr_free <= (num_tx_cmds + QLA_TX_MIN_FREE)) {
930227064Sbz        		QL_DPRINT8((dev, "%s: (hw->txr_free <= "
931227064Sbz				"(num_tx_cmds + QLA_TX_MIN_FREE))\n",
932227064Sbz				__func__));
933227064Sbz			return (-1);
934227064Sbz		}
935227064Sbz	}
936227064Sbz
937227064Sbz	*tx_idx = hw->txr_next;
938227064Sbz
939227064Sbz        tx_cmd = &hw->tx_ring_base[hw->txr_next];
940227064Sbz
941227064Sbz	if (hdr_len == 0) {
942227064Sbz		if ((nsegs > Q8_TX_MAX_SEGMENTS) ||
943227064Sbz			(mp->m_pkthdr.len > ha->max_frame_size)){
944227064Sbz        		device_printf(dev,
945227064Sbz				"%s: (nsegs[%d, %d, 0x%x] > Q8_TX_MAX_SEGMENTS)\n",
946227064Sbz				__func__, nsegs, mp->m_pkthdr.len,
947227064Sbz				mp->m_pkthdr.csum_flags);
948227064Sbz			qla_dump_buf8(ha, "qla_hw_send: wrong pkt",
949227064Sbz				mtod(mp, char *), mp->m_len);
950227064Sbz			return (EINVAL);
951227064Sbz		}
952227064Sbz		bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t));
953227064Sbz		if (qla_tx_chksum(ha, mp, tx_cmd) != 0)
954227064Sbz        		tx_cmd->flags_opcode = Q8_TX_CMD_OP_XMT_ETHER;
955227064Sbz	} else {
956227064Sbz		bcopy(&tso_cmd, tx_cmd, sizeof(q80_tx_cmd_t));
957227064Sbz	}
958227064Sbz
959227064Sbz	eh = mtod(mp, struct ether_vlan_header *);
960227064Sbz	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN))
961227064Sbz        	tx_cmd->flags_opcode |= Q8_TX_CMD_FLAGS_VLAN_TAGGED;
962227064Sbz	else if (mp->m_flags & M_VLANTAG) {
963227064Sbz        	tx_cmd->flags_opcode |= (Q8_TX_CMD_FLAGS_VLAN_TAGGED |
964227064Sbz						Q8_TX_CMD_FLAGS_HW_VLAN_ID);
965227064Sbz		tx_cmd->vlan_tci = mp->m_pkthdr.ether_vtag;
966227064Sbz	}
967227064Sbz
968227064Sbz
969227064Sbz        tx_cmd->n_bufs = (uint8_t)nsegs;
970227064Sbz        tx_cmd->data_len_lo = (uint8_t)(total_length & 0xFF);
971227064Sbz        tx_cmd->data_len_hi = qla_host_to_le16(((uint16_t)(total_length >> 8)));
972227064Sbz	tx_cmd->port_cntxtid = Q8_TX_CMD_PORT_CNXTID(ha->pci_func);
973227064Sbz
974227064Sbz	c_seg = segs;
975227064Sbz
976227064Sbz	while (1) {
977227064Sbz		for (i = 0; ((i < Q8_TX_CMD_MAX_SEGMENTS) && nsegs); i++) {
978227064Sbz
979227064Sbz			switch (i) {
980227064Sbz			case 0:
981227064Sbz				tx_cmd->buf1_addr = c_seg->ds_addr;
982227064Sbz				tx_cmd->buf1_len = c_seg->ds_len;
983227064Sbz				break;
984227064Sbz
985227064Sbz			case 1:
986227064Sbz				tx_cmd->buf2_addr = c_seg->ds_addr;
987227064Sbz				tx_cmd->buf2_len = c_seg->ds_len;
988227064Sbz				break;
989227064Sbz
990227064Sbz			case 2:
991227064Sbz				tx_cmd->buf3_addr = c_seg->ds_addr;
992227064Sbz				tx_cmd->buf3_len = c_seg->ds_len;
993227064Sbz				break;
994227064Sbz
995227064Sbz			case 3:
996227064Sbz				tx_cmd->buf4_addr = c_seg->ds_addr;
997227064Sbz				tx_cmd->buf4_len = c_seg->ds_len;
998227064Sbz				break;
999227064Sbz			}
1000227064Sbz
1001227064Sbz			c_seg++;
1002227064Sbz			nsegs--;
1003227064Sbz		}
1004227064Sbz
1005227064Sbz		hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1);
1006227064Sbz		tx_cmd_count++;
1007227064Sbz
1008227064Sbz		if (!nsegs)
1009227064Sbz			break;
1010227064Sbz
1011227064Sbz        	tx_cmd = &hw->tx_ring_base[hw->txr_next];
1012227064Sbz		bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t));
1013227064Sbz	}
1014227064Sbz
1015227064Sbz	if (hdr_len) {
1016227064Sbz		/* TSO : Copy the header in the following tx cmd descriptors */
1017227064Sbz		uint8_t *src, *dst;
1018227064Sbz
1019227064Sbz		src = (uint8_t *)eh;
1020227064Sbz
1021227064Sbz		tx_cmd = &hw->tx_ring_base[hw->txr_next];
1022227064Sbz		bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t));
1023227064Sbz
1024227064Sbz		bytes = sizeof(q80_tx_cmd_t) - Q8_TX_CMD_TSO_ALIGN;
1025227064Sbz		bytes = QL_MIN(bytes, hdr_len);
1026227064Sbz
1027227064Sbz		dst = (uint8_t *)tx_cmd + Q8_TX_CMD_TSO_ALIGN;
1028227064Sbz
1029227064Sbz		if (mp->m_flags & M_VLANTAG) {
1030227064Sbz			/* first copy the src/dst MAC addresses */
1031227064Sbz			bcopy(src, dst, (ETHER_ADDR_LEN * 2));
1032227064Sbz			dst += (ETHER_ADDR_LEN * 2);
1033227064Sbz			src += (ETHER_ADDR_LEN * 2);
1034227064Sbz
1035227064Sbz			hdr_len -= (ETHER_ADDR_LEN * 2);
1036227064Sbz
1037227064Sbz			*((uint16_t *)dst) = htons(ETHERTYPE_VLAN);
1038227064Sbz			dst += 2;
1039227064Sbz			*((uint16_t *)dst) = mp->m_pkthdr.ether_vtag;
1040227064Sbz			dst += 2;
1041227064Sbz
1042227064Sbz			bytes -= ((ETHER_ADDR_LEN * 2) + 4);
1043227064Sbz
1044227064Sbz			bcopy(src, dst, bytes);
1045227064Sbz			src += bytes;
1046227064Sbz			hdr_len -= bytes;
1047227064Sbz		} else {
1048227064Sbz			bcopy(src, dst, bytes);
1049227064Sbz			src += bytes;
1050227064Sbz			hdr_len -= bytes;
1051227064Sbz		}
1052227064Sbz
1053227064Sbz		hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1);
1054227064Sbz		tx_cmd_count++;
1055227064Sbz
1056227064Sbz		while (hdr_len) {
1057227064Sbz			tx_cmd = &hw->tx_ring_base[hw->txr_next];
1058227064Sbz			bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t));
1059227064Sbz
1060227064Sbz			bytes = QL_MIN((sizeof(q80_tx_cmd_t)), hdr_len);
1061227064Sbz
1062227064Sbz			bcopy(src, tx_cmd, bytes);
1063227064Sbz			src += bytes;
1064227064Sbz			hdr_len -= bytes;
1065227064Sbz			hw->txr_next =
1066227064Sbz				(hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1);
1067227064Sbz			tx_cmd_count++;
1068227064Sbz		}
1069227064Sbz	}
1070227064Sbz
1071227064Sbz	hw->txr_free = hw->txr_free - tx_cmd_count;
1072227064Sbz
1073227064Sbz	QL_UPDATE_TX_PRODUCER_INDEX(ha, hw->txr_next);
1074227064Sbz       	QL_DPRINT8((dev, "%s: return\n", __func__));
1075227064Sbz	return (0);
1076227064Sbz}
1077227064Sbz
1078227064Sbz/*
1079227064Sbz * Name: qla_del_hw_if
1080227064Sbz * Function: Destroys the hardware specific entities corresponding to an
1081227064Sbz *	Ethernet Interface
1082227064Sbz */
1083227064Sbzvoid
1084227064Sbzqla_del_hw_if(qla_host_t *ha)
1085227064Sbz{
1086227064Sbz	int	i;
1087227064Sbz
1088227064Sbz	for (i = 0; i < ha->hw.num_sds_rings; i++)
1089227064Sbz		QL_DISABLE_INTERRUPTS(ha, i);
1090227064Sbz
1091227064Sbz	qla_del_rcv_cntxt(ha);
1092227064Sbz	qla_del_xmt_cntxt(ha);
1093227064Sbz
1094227064Sbz	ha->hw.flags.lro = 0;
1095227064Sbz}
1096227064Sbz
1097227064Sbz/*
1098227064Sbz * Name: qla_init_hw_if
1099227064Sbz * Function: Creates the hardware specific entities corresponding to an
1100227064Sbz *	Ethernet Interface - Transmit and Receive Contexts. Sets the MAC Address
1101227064Sbz *	corresponding to the interface. Enables LRO if allowed.
1102227064Sbz */
1103227064Sbzint
1104227064Sbzqla_init_hw_if(qla_host_t *ha)
1105227064Sbz{
1106227064Sbz	device_t	dev;
1107227064Sbz	int		i;
1108227064Sbz	uint8_t		bcast_mac[6];
1109227064Sbz
1110227064Sbz	qla_get_hw_caps(ha);
1111227064Sbz
1112227064Sbz	dev = ha->pci_dev;
1113227064Sbz
1114227064Sbz	for (i = 0; i < ha->hw.num_sds_rings; i++) {
1115227064Sbz		bzero(ha->hw.dma_buf.sds_ring[i].dma_b,
1116227064Sbz			ha->hw.dma_buf.sds_ring[i].size);
1117227064Sbz	}
1118227064Sbz	/*
1119227064Sbz	 * Create Receive Context
1120227064Sbz	 */
1121227064Sbz	if (qla_init_rcv_cntxt(ha)) {
1122227064Sbz		return (-1);
1123227064Sbz	}
1124227064Sbz
1125227064Sbz	ha->hw.rx_next = NUM_RX_DESCRIPTORS - 2;
1126227064Sbz	ha->hw.rxj_next = NUM_RX_JUMBO_DESCRIPTORS - 2;
1127227064Sbz	ha->hw.rx_in = ha->hw.rxj_in = 0;
1128227064Sbz
1129227064Sbz	/* Update the RDS Producer Indices */
1130227064Sbz	QL_UPDATE_RDS_PRODUCER_INDEX(ha, 0, ha->hw.rx_next);
1131227064Sbz	QL_UPDATE_RDS_PRODUCER_INDEX(ha, 1, ha->hw.rxj_next);
1132227064Sbz
1133227064Sbz	/*
1134227064Sbz	 * Create Transmit Context
1135227064Sbz	 */
1136227064Sbz	if (qla_init_xmt_cntxt(ha)) {
1137227064Sbz		qla_del_rcv_cntxt(ha);
1138227064Sbz		return (-1);
1139227064Sbz	}
1140227064Sbz
1141227064Sbz	qla_config_mac_addr(ha, ha->hw.mac_addr,
1142227064Sbz		(ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 1);
1143227064Sbz
1144227064Sbz	bcast_mac[0] = 0xFF; bcast_mac[1] = 0xFF; bcast_mac[2] = 0xFF;
1145227064Sbz	bcast_mac[3] = 0xFF; bcast_mac[4] = 0xFF; bcast_mac[5] = 0xFF;
1146227064Sbz	qla_config_mac_addr(ha, bcast_mac,
1147227064Sbz		(ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 1);
1148227064Sbz
1149227064Sbz	qla_config_rss(ha, (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id);
1150227064Sbz
1151227064Sbz	qla_config_intr_coalesce(ha, (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 0);
1152227064Sbz
1153227064Sbz	for (i = 0; i < ha->hw.num_sds_rings; i++)
1154227064Sbz		QL_ENABLE_INTERRUPTS(ha, i);
1155227064Sbz
1156227064Sbz	return (0);
1157227064Sbz}
1158227064Sbz
1159227064Sbz/*
1160227064Sbz * Name: qla_init_rcv_cntxt
1161227064Sbz * Function: Creates the Receive Context.
1162227064Sbz */
1163227064Sbzstatic int
1164227064Sbzqla_init_rcv_cntxt(qla_host_t *ha)
1165227064Sbz{
1166227064Sbz	device_t		dev;
1167227064Sbz	qla_cdrp_t		cdrp;
1168227064Sbz	q80_rcv_cntxt_rsp_t	*rsp;
1169227064Sbz	q80_stat_desc_t		*sdesc;
1170227064Sbz	bus_addr_t		phys_addr;
1171227064Sbz	int			i, j;
1172227064Sbz        qla_hw_t		*hw = &ha->hw;
1173227064Sbz
1174227064Sbz	dev = ha->pci_dev;
1175227064Sbz
1176227064Sbz	/*
1177227064Sbz	 * Create Receive Context
1178227064Sbz	 */
1179227064Sbz
1180227064Sbz	for (i = 0; i < hw->num_sds_rings; i++) {
1181227064Sbz		sdesc = (q80_stat_desc_t *)&hw->sds[i].sds_ring_base[0];
1182227064Sbz		for (j = 0; j < NUM_STATUS_DESCRIPTORS; j++) {
1183227064Sbz			sdesc->data[0] =
1184227064Sbz				Q8_STAT_DESC_SET_OWNER(Q8_STAT_DESC_OWNER_FW);
1185227064Sbz		}
1186227064Sbz	}
1187227064Sbz
1188227064Sbz	phys_addr = ha->hw.rx_cntxt_req_paddr;
1189227064Sbz
1190227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1191227064Sbz
1192227064Sbz	cdrp.cmd = Q8_CMD_CREATE_RX_CNTXT;
1193227064Sbz	cdrp.cmd_arg1 = (uint32_t)(phys_addr >> 32);
1194227064Sbz	cdrp.cmd_arg2 = (uint32_t)(phys_addr);
1195227064Sbz	cdrp.cmd_arg3 = (uint32_t)(sizeof (q80_rcv_cntxt_req_t));
1196227064Sbz
1197227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1198227064Sbz		device_printf(dev, "%s: Q8_CMD_CREATE_RX_CNTXT failed\n",
1199227064Sbz			__func__);
1200227064Sbz		return (-1);
1201227064Sbz	} else {
1202227064Sbz		rsp = ha->hw.rx_cntxt_rsp;
1203227064Sbz
1204227064Sbz		QL_DPRINT2((dev, "%s: rcv cntxt successful"
1205227064Sbz			" rds_ring_offset = 0x%08x"
1206227064Sbz			" sds_ring_offset = 0x%08x"
1207227064Sbz			" cntxt_state = 0x%08x"
1208227064Sbz			" funcs_per_port = 0x%08x"
1209227064Sbz			" num_rds_rings = 0x%04x"
1210227064Sbz			" num_sds_rings = 0x%04x"
1211227064Sbz			" cntxt_id = 0x%04x"
1212227064Sbz			" phys_port = 0x%02x"
1213227064Sbz			" virt_port = 0x%02x\n",
1214227064Sbz			__func__,
1215227064Sbz			rsp->rx_rsp.rds_ring_offset,
1216227064Sbz			rsp->rx_rsp.sds_ring_offset,
1217227064Sbz			rsp->rx_rsp.cntxt_state,
1218227064Sbz			rsp->rx_rsp.funcs_per_port,
1219227064Sbz			rsp->rx_rsp.num_rds_rings,
1220227064Sbz			rsp->rx_rsp.num_sds_rings,
1221227064Sbz			rsp->rx_rsp.cntxt_id,
1222227064Sbz			rsp->rx_rsp.phys_port,
1223227064Sbz			rsp->rx_rsp.virt_port));
1224227064Sbz
1225227064Sbz		for (i = 0; i < ha->hw.num_rds_rings; i++) {
1226227064Sbz			QL_DPRINT2((dev,
1227227064Sbz				"%s: rcv cntxt rds[%i].producer_reg = 0x%08x\n",
1228227064Sbz				__func__, i, rsp->rds_rsp[i].producer_reg));
1229227064Sbz		}
1230227064Sbz		for (i = 0; i < ha->hw.num_sds_rings; i++) {
1231227064Sbz			QL_DPRINT2((dev,
1232227064Sbz				"%s: rcv cntxt sds[%i].consumer_reg = 0x%08x"
1233227064Sbz				" sds[%i].intr_mask_reg = 0x%08x\n",
1234227064Sbz				__func__, i, rsp->sds_rsp[i].consumer_reg,
1235227064Sbz				i, rsp->sds_rsp[i].intr_mask_reg));
1236227064Sbz		}
1237227064Sbz	}
1238227064Sbz	ha->hw.flags.init_rx_cnxt = 1;
1239227064Sbz	return (0);
1240227064Sbz}
1241227064Sbz
1242227064Sbz/*
1243227064Sbz * Name: qla_del_rcv_cntxt
1244227064Sbz * Function: Destroys the Receive Context.
1245227064Sbz */
1246227064Sbzvoid
1247227064Sbzqla_del_rcv_cntxt(qla_host_t *ha)
1248227064Sbz{
1249227064Sbz	qla_cdrp_t	cdrp;
1250227064Sbz	device_t	dev = ha->pci_dev;
1251227064Sbz
1252227064Sbz	if (!ha->hw.flags.init_rx_cnxt)
1253227064Sbz		return;
1254227064Sbz
1255227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1256227064Sbz
1257227064Sbz	cdrp.cmd = Q8_CMD_DESTROY_RX_CNTXT;
1258227064Sbz	cdrp.cmd_arg1 = (uint32_t) (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id;
1259227064Sbz
1260227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1261227064Sbz		device_printf(dev, "%s: Q8_CMD_DESTROY_RX_CNTXT failed\n",
1262227064Sbz			__func__);
1263227064Sbz	}
1264227064Sbz	ha->hw.flags.init_rx_cnxt = 0;
1265227064Sbz}
1266227064Sbz
1267227064Sbz/*
1268227064Sbz * Name: qla_init_xmt_cntxt
1269227064Sbz * Function: Creates the Transmit Context.
1270227064Sbz */
1271227064Sbzstatic int
1272227064Sbzqla_init_xmt_cntxt(qla_host_t *ha)
1273227064Sbz{
1274227064Sbz	bus_addr_t		phys_addr;
1275227064Sbz	device_t		dev;
1276227064Sbz	q80_tx_cntxt_rsp_t	*tx_rsp;
1277227064Sbz	qla_cdrp_t		cdrp;
1278227064Sbz        qla_hw_t		*hw = &ha->hw;
1279227064Sbz
1280227064Sbz	dev = ha->pci_dev;
1281227064Sbz
1282227064Sbz	/*
1283227064Sbz	 * Create Transmit Context
1284227064Sbz	 */
1285227064Sbz	phys_addr = ha->hw.tx_cntxt_req_paddr;
1286227064Sbz	tx_rsp = ha->hw.tx_cntxt_rsp;
1287227064Sbz
1288227064Sbz	hw->txr_comp = hw->txr_next = 0;
1289227064Sbz	*(hw->tx_cons) = 0;
1290227064Sbz
1291227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1292227064Sbz
1293227064Sbz	cdrp.cmd = Q8_CMD_CREATE_TX_CNTXT;
1294227064Sbz	cdrp.cmd_arg1 = (uint32_t)(phys_addr >> 32);
1295227064Sbz	cdrp.cmd_arg2 = (uint32_t)(phys_addr);
1296227064Sbz	cdrp.cmd_arg3 = (uint32_t)(sizeof (q80_tx_cntxt_req_t));
1297227064Sbz
1298227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1299227064Sbz		device_printf(dev, "%s: Q8_CMD_CREATE_TX_CNTXT failed\n",
1300227064Sbz			__func__);
1301227064Sbz		return (-1);
1302227064Sbz	} else {
1303227064Sbz		ha->hw.tx_prod_reg = tx_rsp->producer_reg;
1304227064Sbz
1305227064Sbz		QL_DPRINT2((dev, "%s: tx cntxt successful"
1306227064Sbz			" cntxt_state = 0x%08x "
1307227064Sbz			" cntxt_id = 0x%04x "
1308227064Sbz			" phys_port_id = 0x%02x "
1309227064Sbz			" virt_port_id = 0x%02x "
1310227064Sbz			" producer_reg = 0x%08x "
1311227064Sbz			" intr_mask_reg = 0x%08x\n",
1312227064Sbz			__func__, tx_rsp->cntxt_state, tx_rsp->cntxt_id,
1313227064Sbz			tx_rsp->phys_port_id, tx_rsp->virt_port_id,
1314227064Sbz			tx_rsp->producer_reg, tx_rsp->intr_mask_reg));
1315227064Sbz	}
1316227064Sbz	ha->hw.txr_free = NUM_TX_DESCRIPTORS;
1317227064Sbz
1318227064Sbz	ha->hw.flags.init_tx_cnxt = 1;
1319227064Sbz	return (0);
1320227064Sbz}
1321227064Sbz
1322227064Sbz/*
1323227064Sbz * Name: qla_del_xmt_cntxt
1324227064Sbz * Function: Destroys the Transmit Context.
1325227064Sbz */
1326227064Sbzstatic void
1327227064Sbzqla_del_xmt_cntxt(qla_host_t *ha)
1328227064Sbz{
1329227064Sbz	qla_cdrp_t	cdrp;
1330227064Sbz	device_t	dev = ha->pci_dev;
1331227064Sbz
1332227064Sbz	if (!ha->hw.flags.init_tx_cnxt)
1333227064Sbz		return;
1334227064Sbz
1335227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1336227064Sbz
1337227064Sbz	cdrp.cmd = Q8_CMD_DESTROY_TX_CNTXT;
1338227064Sbz	cdrp.cmd_arg1 = (uint32_t) (ha->hw.tx_cntxt_rsp)->cntxt_id;
1339227064Sbz
1340227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1341227064Sbz		device_printf(dev, "%s: Q8_CMD_DESTROY_TX_CNTXT failed\n",
1342227064Sbz			__func__);
1343227064Sbz	}
1344227064Sbz	ha->hw.flags.init_tx_cnxt = 0;
1345227064Sbz}
1346227064Sbz
1347227064Sbz/*
1348227064Sbz * Name: qla_get_max_rds
1349227064Sbz * Function: Returns the maximum number of Receive Descriptor Rings per context.
1350227064Sbz */
1351227064Sbzstatic int
1352227064Sbzqla_get_max_rds(qla_host_t *ha)
1353227064Sbz{
1354227064Sbz	qla_cdrp_t	cdrp;
1355227064Sbz	device_t	dev;
1356227064Sbz
1357227064Sbz	dev = ha->pci_dev;
1358227064Sbz
1359227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1360227064Sbz
1361227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_RDS_PER_CNTXT;
1362227064Sbz
1363227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1364227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_RDS_PER_CNTXT failed\n",
1365227064Sbz			__func__);
1366227064Sbz		return (-1);
1367227064Sbz	} else {
1368227064Sbz		ha->hw.max_rds_per_cntxt = cdrp.rsp_arg1;
1369227064Sbz		QL_DPRINT2((dev, "%s: max_rds_per_context 0x%08x\n",
1370227064Sbz			__func__, ha->hw.max_rds_per_cntxt));
1371227064Sbz	}
1372227064Sbz	return 0;
1373227064Sbz}
1374227064Sbz
1375227064Sbz/*
1376227064Sbz * Name: qla_get_max_sds
1377227064Sbz * Function: Returns the maximum number of Status Descriptor Rings per context.
1378227064Sbz */
1379227064Sbzstatic int
1380227064Sbzqla_get_max_sds(qla_host_t *ha)
1381227064Sbz{
1382227064Sbz	qla_cdrp_t	cdrp;
1383227064Sbz	device_t	dev;
1384227064Sbz
1385227064Sbz	dev = ha->pci_dev;
1386227064Sbz
1387227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1388227064Sbz
1389227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_SDS_PER_CNTXT;
1390227064Sbz
1391227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1392227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_RDS_PER_CNTXT failed\n",
1393227064Sbz			__func__);
1394227064Sbz		return (-1);
1395227064Sbz	} else {
1396227064Sbz		ha->hw.max_sds_per_cntxt = cdrp.rsp_arg1;
1397227064Sbz		QL_DPRINT2((dev, "%s: max_sds_per_context 0x%08x\n",
1398227064Sbz			__func__, ha->hw.max_sds_per_cntxt));
1399227064Sbz	}
1400227064Sbz	return 0;
1401227064Sbz}
1402227064Sbz
1403227064Sbz/*
1404227064Sbz * Name: qla_get_max_rules
1405227064Sbz * Function: Returns the maximum number of Rules per context.
1406227064Sbz */
1407227064Sbzstatic int
1408227064Sbzqla_get_max_rules(qla_host_t *ha)
1409227064Sbz{
1410227064Sbz	qla_cdrp_t	cdrp;
1411227064Sbz	device_t	dev;
1412227064Sbz
1413227064Sbz	dev = ha->pci_dev;
1414227064Sbz
1415227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1416227064Sbz
1417227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_RULES_PER_CNTXT;
1418227064Sbz
1419227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1420227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_RULES_PER_CNTXT failed\n",
1421227064Sbz			__func__);
1422227064Sbz		return (-1);
1423227064Sbz	} else {
1424227064Sbz		ha->hw.max_rules_per_cntxt = cdrp.rsp_arg1;
1425227064Sbz		QL_DPRINT2((dev, "%s: max_rules_per_cntxt 0x%08x\n",
1426227064Sbz			__func__, ha->hw.max_rules_per_cntxt));
1427227064Sbz	}
1428227064Sbz	return 0;
1429227064Sbz}
1430227064Sbz
1431227064Sbz/*
1432227064Sbz * Name: qla_get_max_rcv_cntxts
1433227064Sbz * Function: Returns the maximum number of Receive Contexts supported.
1434227064Sbz */
1435227064Sbzstatic int
1436227064Sbzqla_get_max_rcv_cntxts(qla_host_t *ha)
1437227064Sbz{
1438227064Sbz	qla_cdrp_t	cdrp;
1439227064Sbz	device_t	dev;
1440227064Sbz
1441227064Sbz	dev = ha->pci_dev;
1442227064Sbz
1443227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1444227064Sbz
1445227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_RX_CNTXT;
1446227064Sbz
1447227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1448227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_RX_CNTXT failed\n",
1449227064Sbz			__func__);
1450227064Sbz		return (-1);
1451227064Sbz	} else {
1452227064Sbz		ha->hw.max_rcv_cntxts = cdrp.rsp_arg1;
1453227064Sbz		QL_DPRINT2((dev, "%s: max_rcv_cntxts 0x%08x\n",
1454227064Sbz			__func__, ha->hw.max_rcv_cntxts));
1455227064Sbz	}
1456227064Sbz	return 0;
1457227064Sbz}
1458227064Sbz
1459227064Sbz/*
1460227064Sbz * Name: qla_get_max_tx_cntxts
1461227064Sbz * Function: Returns the maximum number of Transmit Contexts supported.
1462227064Sbz */
1463227064Sbzstatic int
1464227064Sbzqla_get_max_tx_cntxts(qla_host_t *ha)
1465227064Sbz{
1466227064Sbz	qla_cdrp_t	cdrp;
1467227064Sbz	device_t	dev;
1468227064Sbz
1469227064Sbz	dev = ha->pci_dev;
1470227064Sbz
1471227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1472227064Sbz
1473227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_TX_CNTXT;
1474227064Sbz
1475227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1476227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_TX_CNTXT failed\n",
1477227064Sbz			__func__);
1478227064Sbz		return (-1);
1479227064Sbz	} else {
1480227064Sbz		ha->hw.max_xmt_cntxts = cdrp.rsp_arg1;
1481227064Sbz		QL_DPRINT2((dev, "%s: max_xmt_cntxts 0x%08x\n",
1482227064Sbz			__func__, ha->hw.max_xmt_cntxts));
1483227064Sbz	}
1484227064Sbz	return 0;
1485227064Sbz}
1486227064Sbz
1487227064Sbz/*
1488227064Sbz * Name: qla_get_max_mtu
1489227064Sbz * Function: Returns the MTU supported for a context.
1490227064Sbz */
1491227064Sbzstatic int
1492227064Sbzqla_get_max_mtu(qla_host_t *ha)
1493227064Sbz{
1494227064Sbz	qla_cdrp_t	cdrp;
1495227064Sbz	device_t	dev;
1496227064Sbz
1497227064Sbz	dev = ha->pci_dev;
1498227064Sbz
1499227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1500227064Sbz
1501227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_MTU;
1502227064Sbz
1503227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1504227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_MTU failed\n", __func__);
1505227064Sbz		return (-1);
1506227064Sbz	} else {
1507227064Sbz		ha->hw.max_mtu = cdrp.rsp_arg1;
1508227064Sbz		QL_DPRINT2((dev, "%s: max_mtu 0x%08x\n", __func__,
1509227064Sbz			ha->hw.max_mtu));
1510227064Sbz	}
1511227064Sbz	return 0;
1512227064Sbz}
1513227064Sbz
1514227064Sbz/*
1515227064Sbz * Name: qla_set_max_mtu
1516227064Sbz * Function:
1517227064Sbz *	Sets the maximum transfer unit size for the specified rcv context.
1518227064Sbz */
1519227064Sbzint
1520227064Sbzqla_set_max_mtu(qla_host_t *ha, uint32_t mtu, uint16_t cntxt_id)
1521227064Sbz{
1522227064Sbz	qla_cdrp_t	cdrp;
1523227064Sbz	device_t	dev;
1524227064Sbz
1525227064Sbz	dev = ha->pci_dev;
1526227064Sbz
1527227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1528227064Sbz
1529227064Sbz	cdrp.cmd = Q8_CMD_SET_MTU;
1530227064Sbz	cdrp.cmd_arg1 = (uint32_t)cntxt_id;
1531227064Sbz	cdrp.cmd_arg2 = mtu;
1532227064Sbz
1533227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1534227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_MTU failed\n", __func__);
1535227064Sbz		return (-1);
1536227064Sbz	} else {
1537227064Sbz		ha->hw.max_mtu = cdrp.rsp_arg1;
1538227064Sbz	}
1539227064Sbz	return 0;
1540227064Sbz}
1541227064Sbz
1542227064Sbz/*
1543227064Sbz * Name: qla_get_max_lro
1544227064Sbz * Function: Returns the maximum number of TCP Connection which can be supported
1545227064Sbz *	with LRO.
1546227064Sbz */
1547227064Sbzstatic int
1548227064Sbzqla_get_max_lro(qla_host_t *ha)
1549227064Sbz{
1550227064Sbz	qla_cdrp_t	cdrp;
1551227064Sbz	device_t	dev;
1552227064Sbz
1553227064Sbz	dev = ha->pci_dev;
1554227064Sbz
1555227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1556227064Sbz
1557227064Sbz	cdrp.cmd = Q8_CMD_RD_MAX_LRO;
1558227064Sbz
1559227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1560227064Sbz		device_printf(dev, "%s: Q8_CMD_RD_MAX_LRO failed\n", __func__);
1561227064Sbz		return (-1);
1562227064Sbz	} else {
1563227064Sbz		ha->hw.max_lro = cdrp.rsp_arg1;
1564227064Sbz		QL_DPRINT2((dev, "%s: max_lro 0x%08x\n", __func__,
1565227064Sbz			ha->hw.max_lro));
1566227064Sbz	}
1567227064Sbz	return 0;
1568227064Sbz}
1569227064Sbz
1570227064Sbz/*
1571227064Sbz * Name: qla_get_flow_control
1572227064Sbz * Function: Returns the Receive/Transmit Flow Control (PAUSE) settings for
1573227064Sbz *	PCI function.
1574227064Sbz */
1575227064Sbzstatic int
1576227064Sbzqla_get_flow_control(qla_host_t *ha)
1577227064Sbz{
1578227064Sbz	qla_cdrp_t	cdrp;
1579227064Sbz	device_t	dev;
1580227064Sbz
1581227064Sbz	dev = ha->pci_dev;
1582227064Sbz
1583227064Sbz	bzero(&cdrp, sizeof(qla_cdrp_t));
1584227064Sbz
1585227064Sbz	cdrp.cmd = Q8_CMD_GET_FLOW_CNTRL;
1586227064Sbz
1587227064Sbz	if (qla_issue_cmd(ha, &cdrp)) {
1588227064Sbz		device_printf(dev, "%s: Q8_CMD_GET_FLOW_CNTRL failed\n",
1589227064Sbz			__func__);
1590227064Sbz		return (-1);
1591227064Sbz	} else {
1592227064Sbz		QL_DPRINT2((dev, "%s: flow control 0x%08x\n", __func__,
1593227064Sbz			cdrp.rsp_arg1));
1594227064Sbz	}
1595227064Sbz	return 0;
1596227064Sbz}
1597227064Sbz
1598227064Sbz/*
1599227064Sbz * Name: qla_get_flow_control
1600227064Sbz * Function: Retrieves hardware capabilities
1601227064Sbz */
1602227064Sbzvoid
1603227064Sbzqla_get_hw_caps(qla_host_t *ha)
1604227064Sbz{
1605227064Sbz	//qla_read_mac_addr(ha);
1606227064Sbz	qla_get_max_rds(ha);
1607227064Sbz	qla_get_max_sds(ha);
1608227064Sbz	qla_get_max_rules(ha);
1609227064Sbz	qla_get_max_rcv_cntxts(ha);
1610227064Sbz	qla_get_max_tx_cntxts(ha);
1611227064Sbz	qla_get_max_mtu(ha);
1612227064Sbz	qla_get_max_lro(ha);
1613227064Sbz	qla_get_flow_control(ha);
1614227064Sbz	return;
1615227064Sbz}
1616227064Sbz
1617227064Sbz/*
1618227064Sbz * Name: qla_hw_set_multi
1619227064Sbz * Function: Sets the Multicast Addresses provided the host O.S into the
1620227064Sbz *	hardware (for the given interface)
1621227064Sbz */
1622227064Sbzvoid
1623227064Sbzqla_hw_set_multi(qla_host_t *ha, uint8_t *mta, uint32_t mcnt,
1624227064Sbz	uint32_t add_multi)
1625227064Sbz{
1626227064Sbz	q80_rcv_cntxt_rsp_t	*rsp;
1627227064Sbz	int i;
1628227064Sbz
1629227064Sbz	rsp = ha->hw.rx_cntxt_rsp;
1630227064Sbz	for (i = 0; i < mcnt; i++) {
1631227064Sbz		qla_config_mac_addr(ha, mta, rsp->rx_rsp.cntxt_id, add_multi);
1632227064Sbz		mta += Q8_MAC_ADDR_LEN;
1633227064Sbz	}
1634227064Sbz	return;
1635227064Sbz}
1636227064Sbz
1637227064Sbz/*
1638227064Sbz * Name: qla_hw_tx_done_locked
1639227064Sbz * Function: Handle Transmit Completions
1640227064Sbz */
1641227064Sbzstatic void
1642227064Sbzqla_hw_tx_done_locked(qla_host_t *ha)
1643227064Sbz{
1644227064Sbz	qla_tx_buf_t *txb;
1645227064Sbz        qla_hw_t *hw = &ha->hw;
1646227064Sbz	uint32_t comp_idx, comp_count = 0;
1647227064Sbz
1648227064Sbz	/* retrieve index of last entry in tx ring completed */
1649227064Sbz	comp_idx = qla_le32_to_host(*(hw->tx_cons));
1650227064Sbz
1651227064Sbz	while (comp_idx != hw->txr_comp) {
1652227064Sbz
1653227064Sbz		txb = &ha->tx_buf[hw->txr_comp];
1654227064Sbz
1655227064Sbz		hw->txr_comp++;
1656227064Sbz		if (hw->txr_comp == NUM_TX_DESCRIPTORS)
1657227064Sbz			hw->txr_comp = 0;
1658227064Sbz
1659227064Sbz		comp_count++;
1660227064Sbz
1661227064Sbz		if (txb->m_head) {
1662227064Sbz			bus_dmamap_sync(ha->tx_tag, txb->map,
1663227064Sbz				BUS_DMASYNC_POSTWRITE);
1664227064Sbz			bus_dmamap_unload(ha->tx_tag, txb->map);
1665227064Sbz			bus_dmamap_destroy(ha->tx_tag, txb->map);
1666227064Sbz			m_freem(txb->m_head);
1667227064Sbz
1668227064Sbz			txb->map = (bus_dmamap_t)0;
1669227064Sbz			txb->m_head = NULL;
1670227064Sbz		}
1671227064Sbz	}
1672227064Sbz
1673227064Sbz	hw->txr_free += comp_count;
1674227064Sbz
1675227064Sbz       	QL_DPRINT8((ha->pci_dev, "%s: return [c,f, p, pn][%d, %d, %d, %d]\n", __func__,
1676227064Sbz		hw->txr_comp, hw->txr_free, hw->txr_next, READ_REG32(ha, (ha->hw.tx_prod_reg + 0x1b2000))));
1677227064Sbz
1678227064Sbz	return;
1679227064Sbz}
1680227064Sbz
1681227064Sbz/*
1682227064Sbz * Name: qla_hw_tx_done
1683227064Sbz * Function: Handle Transmit Completions
1684227064Sbz */
1685227064Sbzvoid
1686227064Sbzqla_hw_tx_done(qla_host_t *ha)
1687227064Sbz{
1688227064Sbz	if (!mtx_trylock(&ha->tx_lock)) {
1689227064Sbz       		QL_DPRINT8((ha->pci_dev,
1690227064Sbz			"%s: !mtx_trylock(&ha->tx_lock)\n", __func__));
1691227064Sbz		return;
1692227064Sbz	}
1693227064Sbz	qla_hw_tx_done_locked(ha);
1694227064Sbz
1695227064Sbz	if (ha->hw.txr_free > free_pkt_thres)
1696227064Sbz		ha->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1697227064Sbz
1698227064Sbz	mtx_unlock(&ha->tx_lock);
1699227064Sbz	return;
1700227064Sbz}
1701227064Sbz
1702227064Sbzvoid
1703227064Sbzqla_update_link_state(qla_host_t *ha)
1704227064Sbz{
1705227064Sbz	uint32_t link_state;
1706227064Sbz
1707227064Sbz	if (!(ha->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1708227064Sbz		ha->hw.flags.link_up = 0;
1709227064Sbz		return;
1710227064Sbz	}
1711227064Sbz	link_state = READ_REG32(ha, Q8_LINK_STATE);
1712227064Sbz
1713227064Sbz	if (ha->pci_func == 0)
1714227064Sbz		ha->hw.flags.link_up = (((link_state & 0xF) == 1)? 1 : 0);
1715227064Sbz	else
1716227064Sbz		ha->hw.flags.link_up = ((((link_state >> 4)& 0xF) == 1)? 1 : 0);
1717227064Sbz}
1718227064Sbz
1719227064Sbzint
1720227064Sbzqla_config_lro(qla_host_t *ha)
1721227064Sbz{
1722227064Sbz	int i;
1723227064Sbz        qla_hw_t *hw = &ha->hw;
1724227064Sbz	struct lro_ctrl *lro;
1725227064Sbz
1726227064Sbz	for (i = 0; i < hw->num_sds_rings; i++) {
1727227064Sbz		lro = &hw->sds[i].lro;
1728227064Sbz		if (tcp_lro_init(lro)) {
1729227064Sbz			device_printf(ha->pci_dev, "%s: tcp_lro_init failed\n",
1730227064Sbz				__func__);
1731227064Sbz			return (-1);
1732227064Sbz		}
1733227064Sbz		lro->ifp = ha->ifp;
1734227064Sbz	}
1735227064Sbz	ha->flags.lro_init = 1;
1736227064Sbz
1737227064Sbz	QL_DPRINT2((ha->pci_dev, "%s: LRO initialized\n", __func__));
1738227064Sbz	return (0);
1739227064Sbz}
1740227064Sbz
1741227064Sbzvoid
1742227064Sbzqla_free_lro(qla_host_t *ha)
1743227064Sbz{
1744227064Sbz	int i;
1745227064Sbz        qla_hw_t *hw = &ha->hw;
1746227064Sbz	struct lro_ctrl *lro;
1747227064Sbz
1748227064Sbz	if (!ha->flags.lro_init)
1749227064Sbz		return;
1750227064Sbz
1751227064Sbz	for (i = 0; i < hw->num_sds_rings; i++) {
1752227064Sbz		lro = &hw->sds[i].lro;
1753227064Sbz		tcp_lro_free(lro);
1754227064Sbz	}
1755227064Sbz	ha->flags.lro_init = 0;
1756227064Sbz}
1757227064Sbz
1758227064Sbzvoid
1759227064Sbzqla_hw_stop_rcv(qla_host_t *ha)
1760227064Sbz{
1761227064Sbz	int i, done, count = 100;
1762227064Sbz
1763227064Sbz	while (count--) {
1764227064Sbz		done = 1;
1765227064Sbz		for (i = 0; i < ha->hw.num_sds_rings; i++) {
1766227064Sbz			if (ha->hw.sds[i].rcv_active)
1767227064Sbz				done = 0;
1768227064Sbz		}
1769227064Sbz		if (done)
1770227064Sbz			break;
1771227064Sbz		else
1772227064Sbz			qla_mdelay(__func__, 10);
1773227064Sbz	}
1774227064Sbz}
1775227064Sbz
1776