xref: /onnv-gate/usr/src/uts/common/io/myri10ge/drv/myri10ge.c (revision 11878:ac93462db6d7)
110253Sxiuyan.wang@Sun.COM /*
210253Sxiuyan.wang@Sun.COM  * CDDL HEADER START
310253Sxiuyan.wang@Sun.COM  *
410253Sxiuyan.wang@Sun.COM  * The contents of this file are subject to the terms of the
510253Sxiuyan.wang@Sun.COM  * Common Development and Distribution License (the "License").
610253Sxiuyan.wang@Sun.COM  * You may not use this file except in compliance with the License.
710253Sxiuyan.wang@Sun.COM  *
810253Sxiuyan.wang@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910253Sxiuyan.wang@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010253Sxiuyan.wang@Sun.COM  * See the License for the specific language governing permissions
1110253Sxiuyan.wang@Sun.COM  * and limitations under the License.
1210253Sxiuyan.wang@Sun.COM  *
1310253Sxiuyan.wang@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410253Sxiuyan.wang@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510253Sxiuyan.wang@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610253Sxiuyan.wang@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710253Sxiuyan.wang@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810253Sxiuyan.wang@Sun.COM  *
1910253Sxiuyan.wang@Sun.COM  * CDDL HEADER END
2010253Sxiuyan.wang@Sun.COM  */
2110253Sxiuyan.wang@Sun.COM 
2210253Sxiuyan.wang@Sun.COM /*
2311433SMark.Johnson@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2411433SMark.Johnson@Sun.COM  * Use is subject to license terms.
2511433SMark.Johnson@Sun.COM  */
2611433SMark.Johnson@Sun.COM 
2711433SMark.Johnson@Sun.COM /*
2810253Sxiuyan.wang@Sun.COM  * Copyright 2007-2009 Myricom, Inc.  All rights reserved.
2910253Sxiuyan.wang@Sun.COM  * Use is subject to license terms.
3010253Sxiuyan.wang@Sun.COM  */
3110253Sxiuyan.wang@Sun.COM 
3210253Sxiuyan.wang@Sun.COM #ifndef	lint
3310253Sxiuyan.wang@Sun.COM static const char __idstring[] =
3410253Sxiuyan.wang@Sun.COM 	"@(#)$Id: myri10ge.c,v 1.186 2009-06-29 13:47:22 gallatin Exp $";
3510253Sxiuyan.wang@Sun.COM #endif
3610253Sxiuyan.wang@Sun.COM 
3710253Sxiuyan.wang@Sun.COM #define	MXGEFW_NDIS
3810253Sxiuyan.wang@Sun.COM #include "myri10ge_var.h"
3910253Sxiuyan.wang@Sun.COM #include "rss_eth_z8e.h"
4010253Sxiuyan.wang@Sun.COM #include "rss_ethp_z8e.h"
4110253Sxiuyan.wang@Sun.COM #include "mcp_gen_header.h"
4210253Sxiuyan.wang@Sun.COM 
4310253Sxiuyan.wang@Sun.COM #define	MYRI10GE_MAX_ETHER_MTU 9014
4410253Sxiuyan.wang@Sun.COM 
4510253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_STOPPED 0
4610253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_STOPPING 1
4710253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_STARTING 2
4810253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_RUNNING 3
4910253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_OPEN_FAILED 4
5010253Sxiuyan.wang@Sun.COM #define	MYRI10GE_ETH_SUSPENDED_RUNNING 5
5110253Sxiuyan.wang@Sun.COM 
5210253Sxiuyan.wang@Sun.COM static int myri10ge_small_bytes = 510;
5310253Sxiuyan.wang@Sun.COM static int myri10ge_intr_coal_delay = 125;
5410253Sxiuyan.wang@Sun.COM static int myri10ge_flow_control = 1;
5510253Sxiuyan.wang@Sun.COM #if #cpu(i386) || defined __i386 || defined i386 ||	\
5610253Sxiuyan.wang@Sun.COM 	defined __i386__ || #cpu(x86_64) || defined __x86_64__
5710253Sxiuyan.wang@Sun.COM static int myri10ge_nvidia_ecrc_enable = 1;
5810253Sxiuyan.wang@Sun.COM #endif
5910253Sxiuyan.wang@Sun.COM static int myri10ge_mtu_override = 0;
6010253Sxiuyan.wang@Sun.COM static int myri10ge_tx_copylen = 512;
6110253Sxiuyan.wang@Sun.COM static int myri10ge_deassert_wait = 1;
6210253Sxiuyan.wang@Sun.COM static int myri10ge_verbose = 0;
6310253Sxiuyan.wang@Sun.COM static int myri10ge_watchdog_reset = 0;
6410253Sxiuyan.wang@Sun.COM static int myri10ge_use_msix = 1;
6510253Sxiuyan.wang@Sun.COM static int myri10ge_max_slices = -1;
6610253Sxiuyan.wang@Sun.COM static int myri10ge_use_msi = 1;
6710253Sxiuyan.wang@Sun.COM int myri10ge_force_firmware = 0;
6810253Sxiuyan.wang@Sun.COM static boolean_t myri10ge_use_lso = B_TRUE;
6910253Sxiuyan.wang@Sun.COM static int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
7010253Sxiuyan.wang@Sun.COM static int myri10ge_tx_hash = 1;
7111652Sxiuyan.wang@Sun.COM static int myri10ge_lro = 0;
7210253Sxiuyan.wang@Sun.COM static int myri10ge_lro_cnt = 8;
7310253Sxiuyan.wang@Sun.COM int myri10ge_lro_max_aggr = 2;
7410253Sxiuyan.wang@Sun.COM static int myri10ge_lso_copy = 0;
7510253Sxiuyan.wang@Sun.COM static mblk_t *myri10ge_send_wrapper(void *arg, mblk_t *mp);
7610253Sxiuyan.wang@Sun.COM int myri10ge_tx_handles_initial = 128;
7710253Sxiuyan.wang@Sun.COM 
7810253Sxiuyan.wang@Sun.COM static 	kmutex_t myri10ge_param_lock;
7910253Sxiuyan.wang@Sun.COM static void* myri10ge_db_lastfree;
8010253Sxiuyan.wang@Sun.COM 
8110253Sxiuyan.wang@Sun.COM static int myri10ge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
8210253Sxiuyan.wang@Sun.COM static int myri10ge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
8310253Sxiuyan.wang@Sun.COM static int myri10ge_quiesce(dev_info_t *dip);
8410253Sxiuyan.wang@Sun.COM 
8510253Sxiuyan.wang@Sun.COM DDI_DEFINE_STREAM_OPS(myri10ge_ops, nulldev, nulldev, myri10ge_attach,
8610253Sxiuyan.wang@Sun.COM     myri10ge_detach, nodev, NULL, D_MP, NULL, myri10ge_quiesce);
8710253Sxiuyan.wang@Sun.COM 
8810253Sxiuyan.wang@Sun.COM 
8910253Sxiuyan.wang@Sun.COM static struct modldrv modldrv = {
9010253Sxiuyan.wang@Sun.COM 	&mod_driverops,
9110253Sxiuyan.wang@Sun.COM 	"Myricom 10G driver (10GbE)",
9210253Sxiuyan.wang@Sun.COM 	&myri10ge_ops,
9310253Sxiuyan.wang@Sun.COM };
9410253Sxiuyan.wang@Sun.COM 
9510253Sxiuyan.wang@Sun.COM 
9610253Sxiuyan.wang@Sun.COM static struct modlinkage modlinkage = {
9710253Sxiuyan.wang@Sun.COM 	MODREV_1,
9810253Sxiuyan.wang@Sun.COM 	{&modldrv, NULL},
9910253Sxiuyan.wang@Sun.COM };
10010253Sxiuyan.wang@Sun.COM 
10110253Sxiuyan.wang@Sun.COM unsigned char myri10ge_broadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
10210253Sxiuyan.wang@Sun.COM 
10310253Sxiuyan.wang@Sun.COM static ddi_dma_attr_t myri10ge_misc_dma_attr = {
10410253Sxiuyan.wang@Sun.COM 	DMA_ATTR_V0,			/* version number. */
10510253Sxiuyan.wang@Sun.COM 	(uint64_t)0, 			/* low address */
10610253Sxiuyan.wang@Sun.COM 	(uint64_t)0xffffffffffffffffULL, /* high address */
10710253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7ffffff,		/* address counter max */
10810253Sxiuyan.wang@Sun.COM 	(uint64_t)4096,			/* alignment */
10910253Sxiuyan.wang@Sun.COM 	(uint_t)0x7f,			/* burstsizes for 32b and 64b xfers */
11010253Sxiuyan.wang@Sun.COM 	(uint32_t)0x1,			/* minimum transfer size */
11110253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7fffffff,		/* maximum transfer size */
11210253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7fffffff,		/* maximum segment size */
11310253Sxiuyan.wang@Sun.COM 	1,				/* scatter/gather list length */
11410253Sxiuyan.wang@Sun.COM 	1,				/* granularity */
11510253Sxiuyan.wang@Sun.COM 	0				/* attribute flags */
11610253Sxiuyan.wang@Sun.COM };
11710253Sxiuyan.wang@Sun.COM 
11810253Sxiuyan.wang@Sun.COM /*
11910253Sxiuyan.wang@Sun.COM  * The Myri10GE NIC has the following constraints on receive buffers:
12010253Sxiuyan.wang@Sun.COM  * 1) Buffers which cross a 4KB boundary must be aligned to 4KB
12110253Sxiuyan.wang@Sun.COM  * 2) Buffers which are not aligned to 4KB must not cross a 4KB boundary
12210253Sxiuyan.wang@Sun.COM  */
12310253Sxiuyan.wang@Sun.COM 
12410253Sxiuyan.wang@Sun.COM static ddi_dma_attr_t myri10ge_rx_jumbo_dma_attr = {
12510253Sxiuyan.wang@Sun.COM 	DMA_ATTR_V0,			/* version number. */
12610253Sxiuyan.wang@Sun.COM 	(uint64_t)0, 			/* low address */
12710253Sxiuyan.wang@Sun.COM 	(uint64_t)0xffffffffffffffffULL, /* high address */
12810253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7ffffff,		/* address counter max */
12910253Sxiuyan.wang@Sun.COM 	(uint64_t)4096,			/* alignment */
13010253Sxiuyan.wang@Sun.COM 	(uint_t)0x7f,			/* burstsizes for 32b and 64b xfers */
13110253Sxiuyan.wang@Sun.COM 	(uint32_t)0x1,			/* minimum transfer size */
13210253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7fffffff,		/* maximum transfer size */
13310253Sxiuyan.wang@Sun.COM 	UINT64_MAX,			/* maximum segment size */
13410253Sxiuyan.wang@Sun.COM 	1,				/* scatter/gather list length */
13510253Sxiuyan.wang@Sun.COM 	1,				/* granularity */
13610253Sxiuyan.wang@Sun.COM 	0				/* attribute flags */
13710253Sxiuyan.wang@Sun.COM };
13810253Sxiuyan.wang@Sun.COM 
13910253Sxiuyan.wang@Sun.COM static ddi_dma_attr_t myri10ge_rx_std_dma_attr = {
14010253Sxiuyan.wang@Sun.COM 	DMA_ATTR_V0,			/* version number. */
14110253Sxiuyan.wang@Sun.COM 	(uint64_t)0, 			/* low address */
14210253Sxiuyan.wang@Sun.COM 	(uint64_t)0xffffffffffffffffULL, /* high address */
14310253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7ffffff,		/* address counter max */
14410253Sxiuyan.wang@Sun.COM #if defined sparc64 || defined __sparcv9
14510253Sxiuyan.wang@Sun.COM 	(uint64_t)4096,			/* alignment */
14610253Sxiuyan.wang@Sun.COM #else
14710253Sxiuyan.wang@Sun.COM 	(uint64_t)0x80,			/* alignment */
14810253Sxiuyan.wang@Sun.COM #endif
14910253Sxiuyan.wang@Sun.COM 	(uint_t)0x7f,			/* burstsizes for 32b and 64b xfers */
15010253Sxiuyan.wang@Sun.COM 	(uint32_t)0x1,			/* minimum transfer size */
15110253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7fffffff,		/* maximum transfer size */
15210253Sxiuyan.wang@Sun.COM #if defined sparc64 || defined __sparcv9
15310253Sxiuyan.wang@Sun.COM 	UINT64_MAX,			/* maximum segment size */
15410253Sxiuyan.wang@Sun.COM #else
15510253Sxiuyan.wang@Sun.COM 	(uint64_t)0xfff,		/* maximum segment size */
15610253Sxiuyan.wang@Sun.COM #endif
15710253Sxiuyan.wang@Sun.COM 	1,				/* scatter/gather list length */
15810253Sxiuyan.wang@Sun.COM 	1,				/* granularity */
15910253Sxiuyan.wang@Sun.COM 	0				/* attribute flags */
16010253Sxiuyan.wang@Sun.COM };
16110253Sxiuyan.wang@Sun.COM 
16210253Sxiuyan.wang@Sun.COM static ddi_dma_attr_t myri10ge_tx_dma_attr = {
16310253Sxiuyan.wang@Sun.COM 	DMA_ATTR_V0,			/* version number. */
16410253Sxiuyan.wang@Sun.COM 	(uint64_t)0, 			/* low address */
16510253Sxiuyan.wang@Sun.COM 	(uint64_t)0xffffffffffffffffULL, /* high address */
16610253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7ffffff,		/* address counter max */
16710253Sxiuyan.wang@Sun.COM 	(uint64_t)1,			/* alignment */
16810253Sxiuyan.wang@Sun.COM 	(uint_t)0x7f,			/* burstsizes for 32b and 64b xfers */
16910253Sxiuyan.wang@Sun.COM 	(uint32_t)0x1,			/* minimum transfer size */
17010253Sxiuyan.wang@Sun.COM 	(uint64_t)0x7fffffff,		/* maximum transfer size */
17110253Sxiuyan.wang@Sun.COM 	UINT64_MAX,			/* maximum segment size */
17210253Sxiuyan.wang@Sun.COM 	INT32_MAX,			/* scatter/gather list length */
17310253Sxiuyan.wang@Sun.COM 	1,				/* granularity */
17410253Sxiuyan.wang@Sun.COM 	0			/* attribute flags */
17510253Sxiuyan.wang@Sun.COM };
17610253Sxiuyan.wang@Sun.COM 
17710253Sxiuyan.wang@Sun.COM #if defined sparc64 || defined __sparcv9
17810253Sxiuyan.wang@Sun.COM #define	WC 0
17910253Sxiuyan.wang@Sun.COM #else
18010253Sxiuyan.wang@Sun.COM #define	WC 1
18110253Sxiuyan.wang@Sun.COM #endif
18210253Sxiuyan.wang@Sun.COM 
18310253Sxiuyan.wang@Sun.COM struct ddi_device_acc_attr myri10ge_dev_access_attr = {
18410253Sxiuyan.wang@Sun.COM 	DDI_DEVICE_ATTR_V0,		/* version */
18510253Sxiuyan.wang@Sun.COM 	DDI_NEVERSWAP_ACC,		/* endian flash */
18610253Sxiuyan.wang@Sun.COM #if WC
18710253Sxiuyan.wang@Sun.COM 	DDI_MERGING_OK_ACC		/* data order */
18810253Sxiuyan.wang@Sun.COM #else
18910253Sxiuyan.wang@Sun.COM 	DDI_STRICTORDER_ACC
19010253Sxiuyan.wang@Sun.COM #endif
19110253Sxiuyan.wang@Sun.COM };
19210253Sxiuyan.wang@Sun.COM 
19310253Sxiuyan.wang@Sun.COM static void myri10ge_watchdog(void *arg);
19410253Sxiuyan.wang@Sun.COM 
19510253Sxiuyan.wang@Sun.COM #ifdef MYRICOM_PRIV
19610253Sxiuyan.wang@Sun.COM int myri10ge_mtu = MYRI10GE_MAX_ETHER_MTU + MXGEFW_PAD + VLAN_TAGSZ;
19710253Sxiuyan.wang@Sun.COM #else
19810253Sxiuyan.wang@Sun.COM int myri10ge_mtu = ETHERMAX + MXGEFW_PAD + VLAN_TAGSZ;
19910253Sxiuyan.wang@Sun.COM #endif
20010253Sxiuyan.wang@Sun.COM int myri10ge_bigbufs_initial = 1024;
20110253Sxiuyan.wang@Sun.COM int myri10ge_bigbufs_max = 4096;
20210253Sxiuyan.wang@Sun.COM 
20310253Sxiuyan.wang@Sun.COM 
20410253Sxiuyan.wang@Sun.COM caddr_t
myri10ge_dma_alloc(dev_info_t * dip,size_t len,ddi_dma_attr_t * attr,ddi_device_acc_attr_t * accattr,uint_t alloc_flags,int bind_flags,struct myri10ge_dma_stuff * dma,int warn,int (* wait)(caddr_t))20510253Sxiuyan.wang@Sun.COM myri10ge_dma_alloc(dev_info_t *dip, size_t len,
20610253Sxiuyan.wang@Sun.COM     ddi_dma_attr_t *attr, ddi_device_acc_attr_t  *accattr,
20710253Sxiuyan.wang@Sun.COM     uint_t alloc_flags, int bind_flags, struct myri10ge_dma_stuff *dma,
20810253Sxiuyan.wang@Sun.COM     int warn, int (*wait)(caddr_t))
20910253Sxiuyan.wang@Sun.COM {
21010253Sxiuyan.wang@Sun.COM 	caddr_t  kaddr;
21110253Sxiuyan.wang@Sun.COM 	size_t real_length;
21210253Sxiuyan.wang@Sun.COM 	ddi_dma_cookie_t cookie;
21310253Sxiuyan.wang@Sun.COM 	uint_t count;
21410253Sxiuyan.wang@Sun.COM 	int err;
21510253Sxiuyan.wang@Sun.COM 
21610253Sxiuyan.wang@Sun.COM 	err = ddi_dma_alloc_handle(dip, attr, wait,
21710253Sxiuyan.wang@Sun.COM 	    NULL, &dma->handle);
21810253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS) {
21910253Sxiuyan.wang@Sun.COM 		if (warn)
22010253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
22110253Sxiuyan.wang@Sun.COM 			    "myri10ge: ddi_dma_alloc_handle failed\n");
22210253Sxiuyan.wang@Sun.COM 		goto abort_with_nothing;
22310253Sxiuyan.wang@Sun.COM 	}
22410253Sxiuyan.wang@Sun.COM 
22510253Sxiuyan.wang@Sun.COM 	err = ddi_dma_mem_alloc(dma->handle, len, accattr, alloc_flags,
22610253Sxiuyan.wang@Sun.COM 	    wait, NULL, &kaddr, &real_length,
22710253Sxiuyan.wang@Sun.COM 	    &dma->acc_handle);
22810253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS) {
22910253Sxiuyan.wang@Sun.COM 		if (warn)
23010253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
23110253Sxiuyan.wang@Sun.COM 			    "myri10ge: ddi_dma_mem_alloc failed\n");
23210253Sxiuyan.wang@Sun.COM 		goto abort_with_handle;
23310253Sxiuyan.wang@Sun.COM 	}
23410253Sxiuyan.wang@Sun.COM 
23510253Sxiuyan.wang@Sun.COM 	err = ddi_dma_addr_bind_handle(dma->handle, NULL, kaddr, len,
23610253Sxiuyan.wang@Sun.COM 	    bind_flags, wait, NULL, &cookie, &count);
23710253Sxiuyan.wang@Sun.COM 
23810253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS) {
23910253Sxiuyan.wang@Sun.COM 		if (warn)
24010253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
24110253Sxiuyan.wang@Sun.COM 			    "myri10ge: ddi_dma_addr_bind_handle failed\n");
24210253Sxiuyan.wang@Sun.COM 		goto abort_with_mem;
24310253Sxiuyan.wang@Sun.COM 	}
24410253Sxiuyan.wang@Sun.COM 
24510253Sxiuyan.wang@Sun.COM 	if (count != 1) {
24610253Sxiuyan.wang@Sun.COM 		if (warn)
24710253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
24810253Sxiuyan.wang@Sun.COM 			    "myri10ge: got too many dma segments ");
24910253Sxiuyan.wang@Sun.COM 		goto abort_with_bind;
25010253Sxiuyan.wang@Sun.COM 	}
25110253Sxiuyan.wang@Sun.COM 	dma->low = htonl(MYRI10GE_LOWPART_TO_U32(cookie.dmac_laddress));
25210253Sxiuyan.wang@Sun.COM 	dma->high = htonl(MYRI10GE_HIGHPART_TO_U32(cookie.dmac_laddress));
25310253Sxiuyan.wang@Sun.COM 	return (kaddr);
25410253Sxiuyan.wang@Sun.COM 
25510253Sxiuyan.wang@Sun.COM abort_with_bind:
25610253Sxiuyan.wang@Sun.COM 	(void) ddi_dma_unbind_handle(dma->handle);
25710253Sxiuyan.wang@Sun.COM 
25810253Sxiuyan.wang@Sun.COM abort_with_mem:
25910253Sxiuyan.wang@Sun.COM 	ddi_dma_mem_free(&dma->acc_handle);
26010253Sxiuyan.wang@Sun.COM 
26110253Sxiuyan.wang@Sun.COM abort_with_handle:
26210253Sxiuyan.wang@Sun.COM 	ddi_dma_free_handle(&dma->handle);
26310253Sxiuyan.wang@Sun.COM abort_with_nothing:
26410253Sxiuyan.wang@Sun.COM 	if (warn) {
26510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "myri10ge: myri10ge_dma_alloc failed.\n  ");
26610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "args: dip=%p len=0x%lx ddi_dma_attr=%p\n",
26710253Sxiuyan.wang@Sun.COM 		    (void*) dip, len, (void*) attr);
26810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
26910253Sxiuyan.wang@Sun.COM 		    "args: ddi_device_acc_attr=%p  alloc_flags=0x%x\n",
27010253Sxiuyan.wang@Sun.COM 		    (void*) accattr, alloc_flags);
27110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "args: bind_flags=0x%x  dmastuff=%p",
27210253Sxiuyan.wang@Sun.COM 		    bind_flags, (void*) dma);
27310253Sxiuyan.wang@Sun.COM 	}
27410253Sxiuyan.wang@Sun.COM 	return (NULL);
27510253Sxiuyan.wang@Sun.COM 
27610253Sxiuyan.wang@Sun.COM }
27710253Sxiuyan.wang@Sun.COM 
27810253Sxiuyan.wang@Sun.COM void
myri10ge_dma_free(struct myri10ge_dma_stuff * dma)27910253Sxiuyan.wang@Sun.COM myri10ge_dma_free(struct myri10ge_dma_stuff *dma)
28010253Sxiuyan.wang@Sun.COM {
28110253Sxiuyan.wang@Sun.COM 	(void) ddi_dma_unbind_handle(dma->handle);
28210253Sxiuyan.wang@Sun.COM 	ddi_dma_mem_free(&dma->acc_handle);
28310253Sxiuyan.wang@Sun.COM 	ddi_dma_free_handle(&dma->handle);
28410253Sxiuyan.wang@Sun.COM }
28510253Sxiuyan.wang@Sun.COM 
28610253Sxiuyan.wang@Sun.COM static inline void
myri10ge_pio_copy32(void * to,uint32_t * from32,size_t size)28710253Sxiuyan.wang@Sun.COM myri10ge_pio_copy32(void *to, uint32_t *from32, size_t size)
28810253Sxiuyan.wang@Sun.COM {
28910253Sxiuyan.wang@Sun.COM 	register volatile uint32_t *to32;
29010253Sxiuyan.wang@Sun.COM 	size_t i;
29110253Sxiuyan.wang@Sun.COM 
29210253Sxiuyan.wang@Sun.COM 	to32 = (volatile uint32_t *) to;
29310253Sxiuyan.wang@Sun.COM 	for (i = (size / 4); i; i--) {
29410253Sxiuyan.wang@Sun.COM 		*to32 = *from32;
29510253Sxiuyan.wang@Sun.COM 		to32++;
29610253Sxiuyan.wang@Sun.COM 		from32++;
29710253Sxiuyan.wang@Sun.COM 	}
29810253Sxiuyan.wang@Sun.COM }
29910253Sxiuyan.wang@Sun.COM 
30010253Sxiuyan.wang@Sun.COM #if defined(_LP64)
30110253Sxiuyan.wang@Sun.COM static inline void
myri10ge_pio_copy64(void * to,uint64_t * from64,size_t size)30210253Sxiuyan.wang@Sun.COM myri10ge_pio_copy64(void *to, uint64_t *from64, size_t size)
30310253Sxiuyan.wang@Sun.COM {
30410253Sxiuyan.wang@Sun.COM 	register volatile uint64_t *to64;
30510253Sxiuyan.wang@Sun.COM 	size_t i;
30610253Sxiuyan.wang@Sun.COM 
30710253Sxiuyan.wang@Sun.COM 	to64 = (volatile uint64_t *) to;
30810253Sxiuyan.wang@Sun.COM 	for (i = (size / 8); i; i--) {
30910253Sxiuyan.wang@Sun.COM 		*to64 = *from64;
31010253Sxiuyan.wang@Sun.COM 		to64++;
31110253Sxiuyan.wang@Sun.COM 		from64++;
31210253Sxiuyan.wang@Sun.COM 	}
31310253Sxiuyan.wang@Sun.COM }
31410253Sxiuyan.wang@Sun.COM #endif
31510253Sxiuyan.wang@Sun.COM 
31610253Sxiuyan.wang@Sun.COM /*
31710253Sxiuyan.wang@Sun.COM  * This routine copies memory from the host to the NIC.
31810253Sxiuyan.wang@Sun.COM  * The "size" argument must always be a multiple of
31910253Sxiuyan.wang@Sun.COM  * the size of long (4 or 8 bytes), and to/from must also
32010253Sxiuyan.wang@Sun.COM  * be naturally aligned.
32110253Sxiuyan.wang@Sun.COM  */
32210253Sxiuyan.wang@Sun.COM static inline void
myri10ge_pio_copy(void * to,void * from,size_t size)32310253Sxiuyan.wang@Sun.COM myri10ge_pio_copy(void *to, void *from, size_t size)
32410253Sxiuyan.wang@Sun.COM {
32510253Sxiuyan.wang@Sun.COM #if !defined(_LP64)
32610253Sxiuyan.wang@Sun.COM 	ASSERT((size % 4) == 0);
32710253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy32(to, (uint32_t *)from, size);
32810253Sxiuyan.wang@Sun.COM #else
32910253Sxiuyan.wang@Sun.COM 	ASSERT((size % 8) == 0);
33010253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy64(to, (uint64_t *)from, size);
33110253Sxiuyan.wang@Sun.COM #endif
33210253Sxiuyan.wang@Sun.COM }
33310253Sxiuyan.wang@Sun.COM 
33410253Sxiuyan.wang@Sun.COM 
33510253Sxiuyan.wang@Sun.COM /*
33610253Sxiuyan.wang@Sun.COM  * Due to various bugs in Solaris (especially bug 6186772 where the
33710253Sxiuyan.wang@Sun.COM  * TCP/UDP checksum is calculated incorrectly on mblk chains with more
33810253Sxiuyan.wang@Sun.COM  * than two elements), and the design bug where hardware checksums are
33910253Sxiuyan.wang@Sun.COM  * ignored on mblk chains with more than 2 elements, we need to
34010253Sxiuyan.wang@Sun.COM  * allocate private pool of physically contiguous receive buffers.
34110253Sxiuyan.wang@Sun.COM  */
34210253Sxiuyan.wang@Sun.COM 
34310253Sxiuyan.wang@Sun.COM static void
myri10ge_jpool_init(struct myri10ge_slice_state * ss)34410253Sxiuyan.wang@Sun.COM myri10ge_jpool_init(struct myri10ge_slice_state *ss)
34510253Sxiuyan.wang@Sun.COM {
34610253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
34710253Sxiuyan.wang@Sun.COM 
34810253Sxiuyan.wang@Sun.COM 	bzero(jpool, sizeof (*jpool));
34910253Sxiuyan.wang@Sun.COM 	mutex_init(&jpool->mtx, NULL, MUTEX_DRIVER,
35010253Sxiuyan.wang@Sun.COM 	    ss->mgp->icookie);
35110253Sxiuyan.wang@Sun.COM 	jpool->head = NULL;
35210253Sxiuyan.wang@Sun.COM }
35310253Sxiuyan.wang@Sun.COM 
35410253Sxiuyan.wang@Sun.COM static void
myri10ge_jpool_fini(struct myri10ge_slice_state * ss)35510253Sxiuyan.wang@Sun.COM myri10ge_jpool_fini(struct myri10ge_slice_state *ss)
35610253Sxiuyan.wang@Sun.COM {
35710253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
35810253Sxiuyan.wang@Sun.COM 
35910253Sxiuyan.wang@Sun.COM 	if (jpool->head != NULL) {
36010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
36110253Sxiuyan.wang@Sun.COM 		    "%s: BUG! myri10ge_jpool_fini called on non-empty pool\n",
36210253Sxiuyan.wang@Sun.COM 		    ss->mgp->name);
36310253Sxiuyan.wang@Sun.COM 	}
36410253Sxiuyan.wang@Sun.COM 	mutex_destroy(&jpool->mtx);
36510253Sxiuyan.wang@Sun.COM }
36610253Sxiuyan.wang@Sun.COM 
36710253Sxiuyan.wang@Sun.COM 
36810253Sxiuyan.wang@Sun.COM /*
36910253Sxiuyan.wang@Sun.COM  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
37010253Sxiuyan.wang@Sun.COM  * at most 32 bytes at a time, so as to avoid involving the software
37110253Sxiuyan.wang@Sun.COM  * pio handler in the nic.   We re-write the first segment's low
37210253Sxiuyan.wang@Sun.COM  * DMA address to mark it valid only after we write the entire chunk
37310253Sxiuyan.wang@Sun.COM  * in a burst
37410253Sxiuyan.wang@Sun.COM  */
37510253Sxiuyan.wang@Sun.COM static inline void
myri10ge_submit_8rx(mcp_kreq_ether_recv_t * dst,mcp_kreq_ether_recv_t * src)37610253Sxiuyan.wang@Sun.COM myri10ge_submit_8rx(mcp_kreq_ether_recv_t *dst, mcp_kreq_ether_recv_t *src)
37710253Sxiuyan.wang@Sun.COM {
37810253Sxiuyan.wang@Sun.COM 	src->addr_low |= BE_32(1);
37910253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy(dst, src, 4 * sizeof (*src));
38010253Sxiuyan.wang@Sun.COM 	mb();
38110253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
38210253Sxiuyan.wang@Sun.COM 	mb();
38310253Sxiuyan.wang@Sun.COM 	src->addr_low &= ~(BE_32(1));
38410253Sxiuyan.wang@Sun.COM 	dst->addr_low = src->addr_low;
38510253Sxiuyan.wang@Sun.COM 	mb();
38610253Sxiuyan.wang@Sun.COM }
38710253Sxiuyan.wang@Sun.COM 
38810253Sxiuyan.wang@Sun.COM static void
myri10ge_pull_jpool(struct myri10ge_slice_state * ss)38910253Sxiuyan.wang@Sun.COM myri10ge_pull_jpool(struct myri10ge_slice_state *ss)
39010253Sxiuyan.wang@Sun.COM {
39110253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
39210253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *jtail, *j, *jfree;
39310253Sxiuyan.wang@Sun.COM 	volatile uintptr_t *putp;
39410253Sxiuyan.wang@Sun.COM 	uintptr_t put;
39510253Sxiuyan.wang@Sun.COM 	int i;
39610253Sxiuyan.wang@Sun.COM 
39710253Sxiuyan.wang@Sun.COM 	/* find tail */
39810253Sxiuyan.wang@Sun.COM 	jtail = NULL;
39910253Sxiuyan.wang@Sun.COM 	if (jpool->head != NULL) {
40010253Sxiuyan.wang@Sun.COM 		j = jpool->head;
40110253Sxiuyan.wang@Sun.COM 		while (j->next != NULL)
40210253Sxiuyan.wang@Sun.COM 			j = j->next;
40310253Sxiuyan.wang@Sun.COM 		jtail = j;
40410253Sxiuyan.wang@Sun.COM 	}
40510253Sxiuyan.wang@Sun.COM 
40610253Sxiuyan.wang@Sun.COM 	/*
40710253Sxiuyan.wang@Sun.COM 	 * iterate over all per-CPU caches, and add contents into
40810253Sxiuyan.wang@Sun.COM 	 * jpool
40910253Sxiuyan.wang@Sun.COM 	 */
41010253Sxiuyan.wang@Sun.COM 	for (i = 0; i < MYRI10GE_MAX_CPUS; i++) {
41110253Sxiuyan.wang@Sun.COM 		/* take per-CPU free list */
41210253Sxiuyan.wang@Sun.COM 		putp = (void *)&jpool->cpu[i & MYRI10GE_MAX_CPU_MASK].head;
41310253Sxiuyan.wang@Sun.COM 		if (*putp == NULL)
41410253Sxiuyan.wang@Sun.COM 			continue;
41510253Sxiuyan.wang@Sun.COM 		put = atomic_swap_ulong(putp, 0);
41610253Sxiuyan.wang@Sun.COM 		jfree = (struct myri10ge_jpool_entry *)put;
41710253Sxiuyan.wang@Sun.COM 
41810253Sxiuyan.wang@Sun.COM 		/* append to pool */
41910253Sxiuyan.wang@Sun.COM 		if (jtail == NULL) {
42010253Sxiuyan.wang@Sun.COM 			jpool->head = jfree;
42110253Sxiuyan.wang@Sun.COM 		} else {
42210253Sxiuyan.wang@Sun.COM 			jtail->next = jfree;
42310253Sxiuyan.wang@Sun.COM 		}
42410253Sxiuyan.wang@Sun.COM 		j = jfree;
42510253Sxiuyan.wang@Sun.COM 		while (j->next != NULL)
42610253Sxiuyan.wang@Sun.COM 			j = j->next;
42710253Sxiuyan.wang@Sun.COM 		jtail = j;
42810253Sxiuyan.wang@Sun.COM 	}
42910253Sxiuyan.wang@Sun.COM }
43010253Sxiuyan.wang@Sun.COM 
43110253Sxiuyan.wang@Sun.COM /*
43210253Sxiuyan.wang@Sun.COM  * Transfers buffers from the free pool to the nic
43310253Sxiuyan.wang@Sun.COM  * Must be called holding the jpool mutex.
43410253Sxiuyan.wang@Sun.COM  */
43510253Sxiuyan.wang@Sun.COM 
43610253Sxiuyan.wang@Sun.COM static inline void
myri10ge_restock_jumbos(struct myri10ge_slice_state * ss)43710253Sxiuyan.wang@Sun.COM myri10ge_restock_jumbos(struct myri10ge_slice_state *ss)
43810253Sxiuyan.wang@Sun.COM {
43910253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
44010253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
44110253Sxiuyan.wang@Sun.COM 	myri10ge_rx_ring_t *rx;
44210253Sxiuyan.wang@Sun.COM 	int i, idx, limit;
44310253Sxiuyan.wang@Sun.COM 
44410253Sxiuyan.wang@Sun.COM 	rx = &ss->rx_big;
44510253Sxiuyan.wang@Sun.COM 	limit = ss->j_rx_cnt + (rx->mask + 1);
44610253Sxiuyan.wang@Sun.COM 
44710253Sxiuyan.wang@Sun.COM 	for (i = rx->cnt; i != limit; i++) {
44810253Sxiuyan.wang@Sun.COM 		idx = i & (rx->mask);
44910253Sxiuyan.wang@Sun.COM 		j = jpool->head;
45010253Sxiuyan.wang@Sun.COM 		if (j == NULL) {
45110253Sxiuyan.wang@Sun.COM 			myri10ge_pull_jpool(ss);
45210253Sxiuyan.wang@Sun.COM 			j = jpool->head;
45310253Sxiuyan.wang@Sun.COM 			if (j == NULL) {
45410253Sxiuyan.wang@Sun.COM 				break;
45510253Sxiuyan.wang@Sun.COM 			}
45610253Sxiuyan.wang@Sun.COM 		}
45710253Sxiuyan.wang@Sun.COM 		jpool->head = j->next;
45810253Sxiuyan.wang@Sun.COM 		rx->info[idx].j = j;
45910253Sxiuyan.wang@Sun.COM 		rx->shadow[idx].addr_low = j->dma.low;
46010253Sxiuyan.wang@Sun.COM 		rx->shadow[idx].addr_high = j->dma.high;
46110253Sxiuyan.wang@Sun.COM 		/* copy 4 descriptors (32-bytes) to the mcp at a time */
46210253Sxiuyan.wang@Sun.COM 		if ((idx & 7) == 7) {
46310253Sxiuyan.wang@Sun.COM 			myri10ge_submit_8rx(&rx->lanai[idx - 7],
46410253Sxiuyan.wang@Sun.COM 			    &rx->shadow[idx - 7]);
46510253Sxiuyan.wang@Sun.COM 		}
46610253Sxiuyan.wang@Sun.COM 	}
46710253Sxiuyan.wang@Sun.COM 	rx->cnt = i;
46810253Sxiuyan.wang@Sun.COM }
46910253Sxiuyan.wang@Sun.COM 
47010253Sxiuyan.wang@Sun.COM /*
47110253Sxiuyan.wang@Sun.COM  * Transfer buffers from the nic to the free pool.
47210253Sxiuyan.wang@Sun.COM  * Should be called holding the jpool mutex
47310253Sxiuyan.wang@Sun.COM  */
47410253Sxiuyan.wang@Sun.COM 
47510253Sxiuyan.wang@Sun.COM static inline void
myri10ge_unstock_jumbos(struct myri10ge_slice_state * ss)47610253Sxiuyan.wang@Sun.COM myri10ge_unstock_jumbos(struct myri10ge_slice_state *ss)
47710253Sxiuyan.wang@Sun.COM {
47810253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
47910253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
48010253Sxiuyan.wang@Sun.COM 	myri10ge_rx_ring_t *rx;
48110253Sxiuyan.wang@Sun.COM 	int i;
48210253Sxiuyan.wang@Sun.COM 
48310253Sxiuyan.wang@Sun.COM 	mutex_enter(&jpool->mtx);
48410253Sxiuyan.wang@Sun.COM 	rx = &ss->rx_big;
48510253Sxiuyan.wang@Sun.COM 
48610253Sxiuyan.wang@Sun.COM 	for (i = 0; i < rx->mask + 1; i++) {
48710253Sxiuyan.wang@Sun.COM 		j = rx->info[i].j;
48810253Sxiuyan.wang@Sun.COM 		rx->info[i].j = NULL;
48910253Sxiuyan.wang@Sun.COM 		if (j == NULL)
49010253Sxiuyan.wang@Sun.COM 			continue;
49110253Sxiuyan.wang@Sun.COM 		j->next = jpool->head;
49210253Sxiuyan.wang@Sun.COM 		jpool->head = j;
49310253Sxiuyan.wang@Sun.COM 	}
49410253Sxiuyan.wang@Sun.COM 	mutex_exit(&jpool->mtx);
49510253Sxiuyan.wang@Sun.COM 
49610253Sxiuyan.wang@Sun.COM }
49710253Sxiuyan.wang@Sun.COM 
49810253Sxiuyan.wang@Sun.COM 
49910253Sxiuyan.wang@Sun.COM /*
50010253Sxiuyan.wang@Sun.COM  * Free routine which is called when the mblk allocated via
50110253Sxiuyan.wang@Sun.COM  * esballoc() is freed.   Here we return the jumbo buffer
50210253Sxiuyan.wang@Sun.COM  * to the free pool, and possibly pass some jumbo buffers
50310253Sxiuyan.wang@Sun.COM  * to the nic
50410253Sxiuyan.wang@Sun.COM  */
50510253Sxiuyan.wang@Sun.COM 
50610253Sxiuyan.wang@Sun.COM static void
myri10ge_jfree_rtn(void * arg)50710253Sxiuyan.wang@Sun.COM myri10ge_jfree_rtn(void *arg)
50810253Sxiuyan.wang@Sun.COM {
50910253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j = (struct myri10ge_jpool_entry *)arg;
51010253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool;
51110253Sxiuyan.wang@Sun.COM 	volatile uintptr_t *putp;
51210253Sxiuyan.wang@Sun.COM 	uintptr_t old, new;
51310253Sxiuyan.wang@Sun.COM 
51410253Sxiuyan.wang@Sun.COM 	jpool = &j->ss->jpool;
51510253Sxiuyan.wang@Sun.COM 
51610253Sxiuyan.wang@Sun.COM 	/* prepend buffer locklessly to per-CPU freelist */
51710253Sxiuyan.wang@Sun.COM 	putp = (void *)&jpool->cpu[CPU->cpu_seqid & MYRI10GE_MAX_CPU_MASK].head;
51810253Sxiuyan.wang@Sun.COM 	new = (uintptr_t)j;
51910253Sxiuyan.wang@Sun.COM 	do {
52010253Sxiuyan.wang@Sun.COM 		old = *putp;
52110253Sxiuyan.wang@Sun.COM 		j->next = (void *)old;
52210253Sxiuyan.wang@Sun.COM 	} while (atomic_cas_ulong(putp, old, new) != old);
52310253Sxiuyan.wang@Sun.COM }
52410253Sxiuyan.wang@Sun.COM 
52510253Sxiuyan.wang@Sun.COM static void
myri10ge_remove_jbuf(struct myri10ge_jpool_entry * j)52610253Sxiuyan.wang@Sun.COM myri10ge_remove_jbuf(struct myri10ge_jpool_entry *j)
52710253Sxiuyan.wang@Sun.COM {
52810253Sxiuyan.wang@Sun.COM 	(void) ddi_dma_unbind_handle(j->dma_handle);
52910253Sxiuyan.wang@Sun.COM 	ddi_dma_mem_free(&j->acc_handle);
53010253Sxiuyan.wang@Sun.COM 	ddi_dma_free_handle(&j->dma_handle);
53110253Sxiuyan.wang@Sun.COM 	kmem_free(j, sizeof (*j));
53210253Sxiuyan.wang@Sun.COM }
53310253Sxiuyan.wang@Sun.COM 
53410253Sxiuyan.wang@Sun.COM 
53510253Sxiuyan.wang@Sun.COM /*
53610253Sxiuyan.wang@Sun.COM  * Allocates one physically contiguous descriptor
53710253Sxiuyan.wang@Sun.COM  * and add it to the jumbo buffer pool.
53810253Sxiuyan.wang@Sun.COM  */
53910253Sxiuyan.wang@Sun.COM 
54010253Sxiuyan.wang@Sun.COM static int
myri10ge_add_jbuf(struct myri10ge_slice_state * ss)54110253Sxiuyan.wang@Sun.COM myri10ge_add_jbuf(struct myri10ge_slice_state *ss)
54210253Sxiuyan.wang@Sun.COM {
54310253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
54410253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
54510253Sxiuyan.wang@Sun.COM 	ddi_dma_attr_t *rx_dma_attr;
54610253Sxiuyan.wang@Sun.COM 	size_t real_length;
54710253Sxiuyan.wang@Sun.COM 	ddi_dma_cookie_t cookie;
54810253Sxiuyan.wang@Sun.COM 	uint_t count;
54910253Sxiuyan.wang@Sun.COM 	int err;
55010253Sxiuyan.wang@Sun.COM 
55110253Sxiuyan.wang@Sun.COM 	if (myri10ge_mtu < 2048)
55210253Sxiuyan.wang@Sun.COM 		rx_dma_attr = &myri10ge_rx_std_dma_attr;
55310253Sxiuyan.wang@Sun.COM 	else
55410253Sxiuyan.wang@Sun.COM 		rx_dma_attr = &myri10ge_rx_jumbo_dma_attr;
55510253Sxiuyan.wang@Sun.COM 
55610253Sxiuyan.wang@Sun.COM again:
55710253Sxiuyan.wang@Sun.COM 	j = (struct myri10ge_jpool_entry *)
55810253Sxiuyan.wang@Sun.COM 	    kmem_alloc(sizeof (*j), KM_SLEEP);
55910253Sxiuyan.wang@Sun.COM 	err = ddi_dma_alloc_handle(ss->mgp->dip, rx_dma_attr,
56010253Sxiuyan.wang@Sun.COM 	    DDI_DMA_DONTWAIT, NULL, &j->dma_handle);
56110253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS)
56210253Sxiuyan.wang@Sun.COM 		goto abort_with_j;
56310253Sxiuyan.wang@Sun.COM 
56410253Sxiuyan.wang@Sun.COM 	err = ddi_dma_mem_alloc(j->dma_handle, myri10ge_mtu,
56510253Sxiuyan.wang@Sun.COM 	    &myri10ge_dev_access_attr,  DDI_DMA_STREAMING, DDI_DMA_DONTWAIT,
56610253Sxiuyan.wang@Sun.COM 	    NULL, &j->buf, &real_length, &j->acc_handle);
56710253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS)
56810253Sxiuyan.wang@Sun.COM 		goto abort_with_handle;
56910253Sxiuyan.wang@Sun.COM 
57010253Sxiuyan.wang@Sun.COM 	err = ddi_dma_addr_bind_handle(j->dma_handle, NULL, j->buf,
57110253Sxiuyan.wang@Sun.COM 	    real_length, DDI_DMA_READ|DDI_DMA_STREAMING, DDI_DMA_DONTWAIT,
57210253Sxiuyan.wang@Sun.COM 	    NULL, &cookie, &count);
57310253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS)
57410253Sxiuyan.wang@Sun.COM 		goto abort_with_mem;
57510253Sxiuyan.wang@Sun.COM 
57610253Sxiuyan.wang@Sun.COM 	/*
57710253Sxiuyan.wang@Sun.COM 	 * Make certain std MTU buffers do not cross a 4KB boundary:
57810253Sxiuyan.wang@Sun.COM 	 *
57910253Sxiuyan.wang@Sun.COM 	 * Setting dma_attr_align=4096 will do this, but the system
58010253Sxiuyan.wang@Sun.COM 	 * will only allocate 1 RX buffer per 4KB page, rather than 2.
58110253Sxiuyan.wang@Sun.COM 	 * Setting dma_attr_granular=4096 *seems* to work around this,
58210253Sxiuyan.wang@Sun.COM 	 * but I'm paranoid about future systems no longer honoring
58310253Sxiuyan.wang@Sun.COM 	 * this, so fall back to the safe, but memory wasting way if a
58410253Sxiuyan.wang@Sun.COM 	 * buffer crosses a 4KB boundary.
58510253Sxiuyan.wang@Sun.COM 	 */
58610253Sxiuyan.wang@Sun.COM 
58710253Sxiuyan.wang@Sun.COM 	if (rx_dma_attr == &myri10ge_rx_std_dma_attr &&
58810253Sxiuyan.wang@Sun.COM 	    rx_dma_attr->dma_attr_align != 4096) {
58910253Sxiuyan.wang@Sun.COM 		uint32_t start, end;
59010253Sxiuyan.wang@Sun.COM 
59110253Sxiuyan.wang@Sun.COM 		start = MYRI10GE_LOWPART_TO_U32(cookie.dmac_laddress);
59210253Sxiuyan.wang@Sun.COM 		end = start + myri10ge_mtu;
59310253Sxiuyan.wang@Sun.COM 		if (((end >> 12) != (start >> 12)) && (start & 4095U)) {
59410253Sxiuyan.wang@Sun.COM 			printf("std buffer crossed a 4KB boundary!\n");
59510253Sxiuyan.wang@Sun.COM 			myri10ge_remove_jbuf(j);
59610253Sxiuyan.wang@Sun.COM 			rx_dma_attr->dma_attr_align = 4096;
59710253Sxiuyan.wang@Sun.COM 			rx_dma_attr->dma_attr_seg = UINT64_MAX;
59810253Sxiuyan.wang@Sun.COM 			goto again;
59910253Sxiuyan.wang@Sun.COM 		}
60010253Sxiuyan.wang@Sun.COM 	}
60110253Sxiuyan.wang@Sun.COM 
60210253Sxiuyan.wang@Sun.COM 	j->dma.low =
60310253Sxiuyan.wang@Sun.COM 	    htonl(MYRI10GE_LOWPART_TO_U32(cookie.dmac_laddress));
60410253Sxiuyan.wang@Sun.COM 	j->dma.high =
60510253Sxiuyan.wang@Sun.COM 	    htonl(MYRI10GE_HIGHPART_TO_U32(cookie.dmac_laddress));
60610253Sxiuyan.wang@Sun.COM 	j->ss = ss;
60710253Sxiuyan.wang@Sun.COM 
60810253Sxiuyan.wang@Sun.COM 
60910253Sxiuyan.wang@Sun.COM 	j->free_func.free_func = myri10ge_jfree_rtn;
61010253Sxiuyan.wang@Sun.COM 	j->free_func.free_arg = (char *)j;
61110253Sxiuyan.wang@Sun.COM 	mutex_enter(&jpool->mtx);
61210253Sxiuyan.wang@Sun.COM 	j->next = jpool->head;
61310253Sxiuyan.wang@Sun.COM 	jpool->head = j;
61410253Sxiuyan.wang@Sun.COM 	jpool->num_alloc++;
61510253Sxiuyan.wang@Sun.COM 	mutex_exit(&jpool->mtx);
61610253Sxiuyan.wang@Sun.COM 	return (0);
61710253Sxiuyan.wang@Sun.COM 
61810253Sxiuyan.wang@Sun.COM abort_with_mem:
61910253Sxiuyan.wang@Sun.COM 	ddi_dma_mem_free(&j->acc_handle);
62010253Sxiuyan.wang@Sun.COM 
62110253Sxiuyan.wang@Sun.COM abort_with_handle:
62210253Sxiuyan.wang@Sun.COM 	ddi_dma_free_handle(&j->dma_handle);
62310253Sxiuyan.wang@Sun.COM 
62410253Sxiuyan.wang@Sun.COM abort_with_j:
62510253Sxiuyan.wang@Sun.COM 	kmem_free(j, sizeof (*j));
62610253Sxiuyan.wang@Sun.COM 
62710253Sxiuyan.wang@Sun.COM 	/*
62810253Sxiuyan.wang@Sun.COM 	 * If an allocation failed, perhaps it failed because it could
62910253Sxiuyan.wang@Sun.COM 	 * not satisfy granularity requirement.  Disable that, and
63010253Sxiuyan.wang@Sun.COM 	 * try agin.
63110253Sxiuyan.wang@Sun.COM 	 */
63210253Sxiuyan.wang@Sun.COM 	if (rx_dma_attr == &myri10ge_rx_std_dma_attr &&
63310253Sxiuyan.wang@Sun.COM 	    rx_dma_attr->dma_attr_align != 4096) {
63410253Sxiuyan.wang@Sun.COM 			cmn_err(CE_NOTE,
63510253Sxiuyan.wang@Sun.COM 			    "!alloc failed, reverting to gran=1\n");
63610253Sxiuyan.wang@Sun.COM 			rx_dma_attr->dma_attr_align = 4096;
63710253Sxiuyan.wang@Sun.COM 			rx_dma_attr->dma_attr_seg = UINT64_MAX;
63810253Sxiuyan.wang@Sun.COM 			goto again;
63910253Sxiuyan.wang@Sun.COM 	}
64010253Sxiuyan.wang@Sun.COM 	return (err);
64110253Sxiuyan.wang@Sun.COM }
64210253Sxiuyan.wang@Sun.COM 
64310253Sxiuyan.wang@Sun.COM static int
myri10ge_jfree_cnt(struct myri10ge_jpool_stuff * jpool)64410253Sxiuyan.wang@Sun.COM myri10ge_jfree_cnt(struct myri10ge_jpool_stuff *jpool)
64510253Sxiuyan.wang@Sun.COM {
64610253Sxiuyan.wang@Sun.COM 	int i;
64710253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
64810253Sxiuyan.wang@Sun.COM 
64910253Sxiuyan.wang@Sun.COM 	mutex_enter(&jpool->mtx);
65010253Sxiuyan.wang@Sun.COM 	j = jpool->head;
65110253Sxiuyan.wang@Sun.COM 	i = 0;
65210253Sxiuyan.wang@Sun.COM 	while (j != NULL) {
65310253Sxiuyan.wang@Sun.COM 		i++;
65410253Sxiuyan.wang@Sun.COM 		j = j->next;
65510253Sxiuyan.wang@Sun.COM 	}
65610253Sxiuyan.wang@Sun.COM 	mutex_exit(&jpool->mtx);
65710253Sxiuyan.wang@Sun.COM 	return (i);
65810253Sxiuyan.wang@Sun.COM }
65910253Sxiuyan.wang@Sun.COM 
66010253Sxiuyan.wang@Sun.COM static int
myri10ge_add_jbufs(struct myri10ge_slice_state * ss,int num,int total)66110253Sxiuyan.wang@Sun.COM myri10ge_add_jbufs(struct myri10ge_slice_state *ss, int num, int total)
66210253Sxiuyan.wang@Sun.COM {
66310253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
66410253Sxiuyan.wang@Sun.COM 	int allocated = 0;
66510253Sxiuyan.wang@Sun.COM 	int err;
66610253Sxiuyan.wang@Sun.COM 	int needed;
66710253Sxiuyan.wang@Sun.COM 
66810253Sxiuyan.wang@Sun.COM 	/*
66910253Sxiuyan.wang@Sun.COM 	 * if total is set, user wants "num" jbufs in the pool,
67010253Sxiuyan.wang@Sun.COM 	 * otherwise the user wants to "num" additional jbufs
67110253Sxiuyan.wang@Sun.COM 	 * added to the pool
67210253Sxiuyan.wang@Sun.COM 	 */
67310253Sxiuyan.wang@Sun.COM 	if (total && jpool->num_alloc) {
67410253Sxiuyan.wang@Sun.COM 		allocated = myri10ge_jfree_cnt(jpool);
67510253Sxiuyan.wang@Sun.COM 		needed = num - allocated;
67610253Sxiuyan.wang@Sun.COM 	} else {
67710253Sxiuyan.wang@Sun.COM 		needed = num;
67810253Sxiuyan.wang@Sun.COM 	}
67910253Sxiuyan.wang@Sun.COM 
68010253Sxiuyan.wang@Sun.COM 	while (needed > 0) {
68110253Sxiuyan.wang@Sun.COM 		needed--;
68210253Sxiuyan.wang@Sun.COM 		err = myri10ge_add_jbuf(ss);
68310253Sxiuyan.wang@Sun.COM 		if (err == 0) {
68410253Sxiuyan.wang@Sun.COM 			allocated++;
68510253Sxiuyan.wang@Sun.COM 		}
68610253Sxiuyan.wang@Sun.COM 	}
68710253Sxiuyan.wang@Sun.COM 	return (allocated);
68810253Sxiuyan.wang@Sun.COM }
68910253Sxiuyan.wang@Sun.COM 
69010253Sxiuyan.wang@Sun.COM static void
myri10ge_remove_jbufs(struct myri10ge_slice_state * ss)69110253Sxiuyan.wang@Sun.COM myri10ge_remove_jbufs(struct myri10ge_slice_state *ss)
69210253Sxiuyan.wang@Sun.COM {
69310253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
69410253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
69510253Sxiuyan.wang@Sun.COM 
69610253Sxiuyan.wang@Sun.COM 	mutex_enter(&jpool->mtx);
69710253Sxiuyan.wang@Sun.COM 	myri10ge_pull_jpool(ss);
69810253Sxiuyan.wang@Sun.COM 	while (jpool->head != NULL) {
69910253Sxiuyan.wang@Sun.COM 		jpool->num_alloc--;
70010253Sxiuyan.wang@Sun.COM 		j = jpool->head;
70110253Sxiuyan.wang@Sun.COM 		jpool->head = j->next;
70210253Sxiuyan.wang@Sun.COM 		myri10ge_remove_jbuf(j);
70310253Sxiuyan.wang@Sun.COM 	}
70410253Sxiuyan.wang@Sun.COM 	mutex_exit(&jpool->mtx);
70510253Sxiuyan.wang@Sun.COM }
70610253Sxiuyan.wang@Sun.COM 
70710253Sxiuyan.wang@Sun.COM static void
myri10ge_carve_up_jbufs_into_small_ring(struct myri10ge_slice_state * ss)70810253Sxiuyan.wang@Sun.COM myri10ge_carve_up_jbufs_into_small_ring(struct myri10ge_slice_state *ss)
70910253Sxiuyan.wang@Sun.COM {
71010253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
71110253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j = NULL;
71210253Sxiuyan.wang@Sun.COM 	caddr_t ptr;
71310253Sxiuyan.wang@Sun.COM 	uint32_t dma_low, dma_high;
71410253Sxiuyan.wang@Sun.COM 	int idx, len;
71510253Sxiuyan.wang@Sun.COM 	unsigned int alloc_size;
71610253Sxiuyan.wang@Sun.COM 
71710253Sxiuyan.wang@Sun.COM 	dma_low = dma_high = len = 0;
71810253Sxiuyan.wang@Sun.COM 	alloc_size = myri10ge_small_bytes + MXGEFW_PAD;
71910253Sxiuyan.wang@Sun.COM 	ptr = NULL;
72010253Sxiuyan.wang@Sun.COM 	for (idx = 0; idx < ss->rx_small.mask + 1; idx++) {
72110253Sxiuyan.wang@Sun.COM 		/* Allocate a jumbo frame and carve it into small frames */
72210253Sxiuyan.wang@Sun.COM 		if (len < alloc_size) {
72310253Sxiuyan.wang@Sun.COM 			mutex_enter(&jpool->mtx);
72410253Sxiuyan.wang@Sun.COM 			/* remove jumbo from freelist */
72510253Sxiuyan.wang@Sun.COM 			j = jpool->head;
72610253Sxiuyan.wang@Sun.COM 			jpool->head = j->next;
72710253Sxiuyan.wang@Sun.COM 			/* place it onto small list */
72810253Sxiuyan.wang@Sun.COM 			j->next = ss->small_jpool;
72910253Sxiuyan.wang@Sun.COM 			ss->small_jpool = j;
73010253Sxiuyan.wang@Sun.COM 			mutex_exit(&jpool->mtx);
73110253Sxiuyan.wang@Sun.COM 			len = myri10ge_mtu;
73210253Sxiuyan.wang@Sun.COM 			dma_low = ntohl(j->dma.low);
73310253Sxiuyan.wang@Sun.COM 			dma_high = ntohl(j->dma.high);
73410253Sxiuyan.wang@Sun.COM 			ptr = j->buf;
73510253Sxiuyan.wang@Sun.COM 		}
73610253Sxiuyan.wang@Sun.COM 		ss->rx_small.info[idx].ptr = ptr;
73710253Sxiuyan.wang@Sun.COM 		ss->rx_small.shadow[idx].addr_low = htonl(dma_low);
73810253Sxiuyan.wang@Sun.COM 		ss->rx_small.shadow[idx].addr_high = htonl(dma_high);
73910253Sxiuyan.wang@Sun.COM 		len -= alloc_size;
74010253Sxiuyan.wang@Sun.COM 		ptr += alloc_size;
74110253Sxiuyan.wang@Sun.COM 		dma_low += alloc_size;
74210253Sxiuyan.wang@Sun.COM 	}
74310253Sxiuyan.wang@Sun.COM }
74410253Sxiuyan.wang@Sun.COM 
74510253Sxiuyan.wang@Sun.COM /*
74610253Sxiuyan.wang@Sun.COM  * Return the jumbo bufs we carved up for small to the jumbo pool
74710253Sxiuyan.wang@Sun.COM  */
74810253Sxiuyan.wang@Sun.COM 
74910253Sxiuyan.wang@Sun.COM static void
myri10ge_release_small_jbufs(struct myri10ge_slice_state * ss)75010253Sxiuyan.wang@Sun.COM myri10ge_release_small_jbufs(struct myri10ge_slice_state *ss)
75110253Sxiuyan.wang@Sun.COM {
75210253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool = &ss->jpool;
75310253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j = NULL;
75410253Sxiuyan.wang@Sun.COM 
75510253Sxiuyan.wang@Sun.COM 	mutex_enter(&jpool->mtx);
75610253Sxiuyan.wang@Sun.COM 	while (ss->small_jpool != NULL) {
75710253Sxiuyan.wang@Sun.COM 		j = ss->small_jpool;
75810253Sxiuyan.wang@Sun.COM 		ss->small_jpool = j->next;
75910253Sxiuyan.wang@Sun.COM 		j->next = jpool->head;
76010253Sxiuyan.wang@Sun.COM 		jpool->head = j;
76110253Sxiuyan.wang@Sun.COM 	}
76210253Sxiuyan.wang@Sun.COM 	mutex_exit(&jpool->mtx);
76310253Sxiuyan.wang@Sun.COM 	ss->jbufs_for_smalls = 0;
76410253Sxiuyan.wang@Sun.COM }
76510253Sxiuyan.wang@Sun.COM 
76610253Sxiuyan.wang@Sun.COM static int
myri10ge_add_tx_handle(struct myri10ge_slice_state * ss)76710253Sxiuyan.wang@Sun.COM myri10ge_add_tx_handle(struct myri10ge_slice_state *ss)
76810253Sxiuyan.wang@Sun.COM {
76910253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
77010253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
77110253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle *handle;
77210253Sxiuyan.wang@Sun.COM 	int err;
77310253Sxiuyan.wang@Sun.COM 
77410253Sxiuyan.wang@Sun.COM 	handle = kmem_zalloc(sizeof (*handle), KM_SLEEP);
77510253Sxiuyan.wang@Sun.COM 	err = ddi_dma_alloc_handle(mgp->dip,
77610253Sxiuyan.wang@Sun.COM 	    &myri10ge_tx_dma_attr,
77710253Sxiuyan.wang@Sun.COM 	    DDI_DMA_SLEEP, NULL,
77810253Sxiuyan.wang@Sun.COM 	    &handle->h);
77910253Sxiuyan.wang@Sun.COM 	if (err) {
78010253Sxiuyan.wang@Sun.COM 		static int limit = 0;
78110253Sxiuyan.wang@Sun.COM 		if (limit == 0)
78210253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN, "%s: Falled to alloc tx dma handle\n",
78310253Sxiuyan.wang@Sun.COM 			    mgp->name);
78410253Sxiuyan.wang@Sun.COM 		limit++;
78510253Sxiuyan.wang@Sun.COM 		kmem_free(handle, sizeof (*handle));
78610253Sxiuyan.wang@Sun.COM 		return (err);
78710253Sxiuyan.wang@Sun.COM 	}
78810253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->handle_lock);
78910253Sxiuyan.wang@Sun.COM 	MYRI10GE_SLICE_STAT_INC(tx_handles_alloced);
79010253Sxiuyan.wang@Sun.COM 	handle->next = tx->free_tx_handles;
79110253Sxiuyan.wang@Sun.COM 	tx->free_tx_handles = handle;
79210253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->handle_lock);
79310253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
79410253Sxiuyan.wang@Sun.COM }
79510253Sxiuyan.wang@Sun.COM 
79610253Sxiuyan.wang@Sun.COM static void
myri10ge_remove_tx_handles(struct myri10ge_slice_state * ss)79710253Sxiuyan.wang@Sun.COM myri10ge_remove_tx_handles(struct myri10ge_slice_state *ss)
79810253Sxiuyan.wang@Sun.COM {
79910253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
80010253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle *handle;
80110253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->handle_lock);
80210253Sxiuyan.wang@Sun.COM 
80310253Sxiuyan.wang@Sun.COM 	handle = tx->free_tx_handles;
80410253Sxiuyan.wang@Sun.COM 	while (handle != NULL) {
80510253Sxiuyan.wang@Sun.COM 		tx->free_tx_handles = handle->next;
80610253Sxiuyan.wang@Sun.COM 		ddi_dma_free_handle(&handle->h);
80710253Sxiuyan.wang@Sun.COM 		kmem_free(handle, sizeof (*handle));
80810253Sxiuyan.wang@Sun.COM 		handle = tx->free_tx_handles;
80910253Sxiuyan.wang@Sun.COM 		MYRI10GE_SLICE_STAT_DEC(tx_handles_alloced);
81010253Sxiuyan.wang@Sun.COM 	}
81110253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->handle_lock);
81210253Sxiuyan.wang@Sun.COM 	if (MYRI10GE_SLICE_STAT(tx_handles_alloced) != 0) {
81310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: %d tx dma handles allocated at close\n",
81410253Sxiuyan.wang@Sun.COM 		    ss->mgp->name,
81510253Sxiuyan.wang@Sun.COM 		    (int)MYRI10GE_SLICE_STAT(tx_handles_alloced));
81610253Sxiuyan.wang@Sun.COM 	}
81710253Sxiuyan.wang@Sun.COM }
81810253Sxiuyan.wang@Sun.COM 
81910253Sxiuyan.wang@Sun.COM static void
myri10ge_free_tx_handles(myri10ge_tx_ring_t * tx,struct myri10ge_tx_dma_handle_head * list)82010253Sxiuyan.wang@Sun.COM myri10ge_free_tx_handles(myri10ge_tx_ring_t *tx,
82110253Sxiuyan.wang@Sun.COM     struct myri10ge_tx_dma_handle_head *list)
82210253Sxiuyan.wang@Sun.COM {
82310253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->handle_lock);
82410253Sxiuyan.wang@Sun.COM 	list->tail->next = tx->free_tx_handles;
82510253Sxiuyan.wang@Sun.COM 	tx->free_tx_handles = list->head;
82610253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->handle_lock);
82710253Sxiuyan.wang@Sun.COM }
82810253Sxiuyan.wang@Sun.COM 
82910253Sxiuyan.wang@Sun.COM static void
myri10ge_free_tx_handle_slist(myri10ge_tx_ring_t * tx,struct myri10ge_tx_dma_handle * handle)83010253Sxiuyan.wang@Sun.COM myri10ge_free_tx_handle_slist(myri10ge_tx_ring_t *tx,
83110253Sxiuyan.wang@Sun.COM     struct myri10ge_tx_dma_handle *handle)
83210253Sxiuyan.wang@Sun.COM {
83310253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle_head list;
83410253Sxiuyan.wang@Sun.COM 
83510253Sxiuyan.wang@Sun.COM 	if (handle == NULL)
83610253Sxiuyan.wang@Sun.COM 		return;
83710253Sxiuyan.wang@Sun.COM 	list.head = handle;
83810253Sxiuyan.wang@Sun.COM 	list.tail = handle;
83910253Sxiuyan.wang@Sun.COM 	while (handle != NULL) {
84010253Sxiuyan.wang@Sun.COM 		list.tail = handle;
84110253Sxiuyan.wang@Sun.COM 		handle = handle->next;
84210253Sxiuyan.wang@Sun.COM 	}
84310253Sxiuyan.wang@Sun.COM 	myri10ge_free_tx_handles(tx, &list);
84410253Sxiuyan.wang@Sun.COM }
84510253Sxiuyan.wang@Sun.COM 
84610253Sxiuyan.wang@Sun.COM static int
myri10ge_alloc_tx_handles(struct myri10ge_slice_state * ss,int count,struct myri10ge_tx_dma_handle ** ret)84710253Sxiuyan.wang@Sun.COM myri10ge_alloc_tx_handles(struct myri10ge_slice_state *ss, int count,
84810253Sxiuyan.wang@Sun.COM     struct myri10ge_tx_dma_handle **ret)
84910253Sxiuyan.wang@Sun.COM {
85010253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
85110253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle *handle;
85210253Sxiuyan.wang@Sun.COM 	int err, i;
85310253Sxiuyan.wang@Sun.COM 
85410253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->handle_lock);
85510253Sxiuyan.wang@Sun.COM 	for (i = 0; i < count; i++) {
85610253Sxiuyan.wang@Sun.COM 		handle = tx->free_tx_handles;
85710253Sxiuyan.wang@Sun.COM 		while (handle == NULL) {
85810253Sxiuyan.wang@Sun.COM 			mutex_exit(&tx->handle_lock);
85910253Sxiuyan.wang@Sun.COM 			err = myri10ge_add_tx_handle(ss);
86010253Sxiuyan.wang@Sun.COM 			if (err != DDI_SUCCESS) {
86110253Sxiuyan.wang@Sun.COM 				goto abort_with_handles;
86210253Sxiuyan.wang@Sun.COM 			}
86310253Sxiuyan.wang@Sun.COM 			mutex_enter(&tx->handle_lock);
86410253Sxiuyan.wang@Sun.COM 			handle = tx->free_tx_handles;
86510253Sxiuyan.wang@Sun.COM 		}
86610253Sxiuyan.wang@Sun.COM 		tx->free_tx_handles = handle->next;
86710253Sxiuyan.wang@Sun.COM 		handle->next = *ret;
86810253Sxiuyan.wang@Sun.COM 		*ret = handle;
86910253Sxiuyan.wang@Sun.COM 	}
87010253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->handle_lock);
87110253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
87210253Sxiuyan.wang@Sun.COM 
87310253Sxiuyan.wang@Sun.COM abort_with_handles:
87410253Sxiuyan.wang@Sun.COM 	myri10ge_free_tx_handle_slist(tx, *ret);
87510253Sxiuyan.wang@Sun.COM 	return (err);
87610253Sxiuyan.wang@Sun.COM }
87710253Sxiuyan.wang@Sun.COM 
87810253Sxiuyan.wang@Sun.COM 
87910253Sxiuyan.wang@Sun.COM /*
88010253Sxiuyan.wang@Sun.COM  * Frees DMA resources associated with the send ring
88110253Sxiuyan.wang@Sun.COM  */
88210253Sxiuyan.wang@Sun.COM static void
myri10ge_unprepare_tx_ring(struct myri10ge_slice_state * ss)88310253Sxiuyan.wang@Sun.COM myri10ge_unprepare_tx_ring(struct myri10ge_slice_state *ss)
88410253Sxiuyan.wang@Sun.COM {
88510253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx;
88610253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle_head handles;
88710253Sxiuyan.wang@Sun.COM 	size_t bytes;
88810253Sxiuyan.wang@Sun.COM 	int idx;
88910253Sxiuyan.wang@Sun.COM 
89010253Sxiuyan.wang@Sun.COM 	tx = &ss->tx;
89110253Sxiuyan.wang@Sun.COM 	handles.head = NULL;
89210253Sxiuyan.wang@Sun.COM 	handles.tail = NULL;
89310253Sxiuyan.wang@Sun.COM 	for (idx = 0; idx < ss->tx.mask + 1; idx++) {
89410253Sxiuyan.wang@Sun.COM 		if (tx->info[idx].m) {
89510253Sxiuyan.wang@Sun.COM 			(void) ddi_dma_unbind_handle(tx->info[idx].handle->h);
89610253Sxiuyan.wang@Sun.COM 			handles.head = tx->info[idx].handle;
89710253Sxiuyan.wang@Sun.COM 			if (handles.tail == NULL)
89810253Sxiuyan.wang@Sun.COM 				handles.tail = tx->info[idx].handle;
89910253Sxiuyan.wang@Sun.COM 			freeb(tx->info[idx].m);
90010253Sxiuyan.wang@Sun.COM 			tx->info[idx].m = 0;
90110253Sxiuyan.wang@Sun.COM 			tx->info[idx].handle = 0;
90210253Sxiuyan.wang@Sun.COM 		}
90310253Sxiuyan.wang@Sun.COM 		tx->cp[idx].va = NULL;
90410253Sxiuyan.wang@Sun.COM 		myri10ge_dma_free(&tx->cp[idx].dma);
90510253Sxiuyan.wang@Sun.COM 	}
90610253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*tx->cp) * (tx->mask + 1);
90710253Sxiuyan.wang@Sun.COM 	kmem_free(tx->cp, bytes);
90810253Sxiuyan.wang@Sun.COM 	tx->cp = NULL;
90910253Sxiuyan.wang@Sun.COM 	if (handles.head != NULL)
91010253Sxiuyan.wang@Sun.COM 		myri10ge_free_tx_handles(tx, &handles);
91110253Sxiuyan.wang@Sun.COM 	myri10ge_remove_tx_handles(ss);
91210253Sxiuyan.wang@Sun.COM }
91310253Sxiuyan.wang@Sun.COM 
91410253Sxiuyan.wang@Sun.COM /*
91510253Sxiuyan.wang@Sun.COM  * Allocates DMA handles associated with the send ring
91610253Sxiuyan.wang@Sun.COM  */
91710253Sxiuyan.wang@Sun.COM static inline int
myri10ge_prepare_tx_ring(struct myri10ge_slice_state * ss)91810253Sxiuyan.wang@Sun.COM myri10ge_prepare_tx_ring(struct myri10ge_slice_state *ss)
91910253Sxiuyan.wang@Sun.COM {
92010253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle *handles;
92110253Sxiuyan.wang@Sun.COM 	int h;
92210253Sxiuyan.wang@Sun.COM 	size_t bytes;
92310253Sxiuyan.wang@Sun.COM 
92410253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*ss->tx.cp) * (ss->tx.mask + 1);
92510253Sxiuyan.wang@Sun.COM 	ss->tx.cp = kmem_zalloc(bytes, KM_SLEEP);
92610253Sxiuyan.wang@Sun.COM 	if (ss->tx.cp == NULL) {
92710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
92810253Sxiuyan.wang@Sun.COM 		    "%s: Failed to allocate tx copyblock storage\n",
92910253Sxiuyan.wang@Sun.COM 		    ss->mgp->name);
93010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
93110253Sxiuyan.wang@Sun.COM 	}
93210253Sxiuyan.wang@Sun.COM 
93310253Sxiuyan.wang@Sun.COM 
93410253Sxiuyan.wang@Sun.COM 	/* allocate the TX copyblocks */
93510253Sxiuyan.wang@Sun.COM 	for (h = 0; h < ss->tx.mask + 1; h++) {
93610253Sxiuyan.wang@Sun.COM 		ss->tx.cp[h].va = myri10ge_dma_alloc(ss->mgp->dip,
93710253Sxiuyan.wang@Sun.COM 		    4096, &myri10ge_rx_jumbo_dma_attr,
93810253Sxiuyan.wang@Sun.COM 		    &myri10ge_dev_access_attr, DDI_DMA_STREAMING,
93910253Sxiuyan.wang@Sun.COM 		    DDI_DMA_WRITE|DDI_DMA_STREAMING, &ss->tx.cp[h].dma, 1,
94010253Sxiuyan.wang@Sun.COM 		    DDI_DMA_DONTWAIT);
94110253Sxiuyan.wang@Sun.COM 		if (ss->tx.cp[h].va == NULL) {
94210253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN, "%s: Failed to allocate tx "
94310253Sxiuyan.wang@Sun.COM 			    "copyblock %d\n", ss->mgp->name, h);
94410253Sxiuyan.wang@Sun.COM 			goto abort_with_copyblocks;
94510253Sxiuyan.wang@Sun.COM 		}
94610253Sxiuyan.wang@Sun.COM 	}
94710253Sxiuyan.wang@Sun.COM 	/* pre-allocate transmit handles */
94810253Sxiuyan.wang@Sun.COM 	handles = NULL;
94910253Sxiuyan.wang@Sun.COM 	(void) myri10ge_alloc_tx_handles(ss, myri10ge_tx_handles_initial,
95010253Sxiuyan.wang@Sun.COM 	    &handles);
95110253Sxiuyan.wang@Sun.COM 	if (handles != NULL)
95210253Sxiuyan.wang@Sun.COM 		myri10ge_free_tx_handle_slist(&ss->tx, handles);
95310253Sxiuyan.wang@Sun.COM 
95410253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
95510253Sxiuyan.wang@Sun.COM 
95610253Sxiuyan.wang@Sun.COM abort_with_copyblocks:
95710253Sxiuyan.wang@Sun.COM 	while (h > 0)  {
95810253Sxiuyan.wang@Sun.COM 		h--;
95910253Sxiuyan.wang@Sun.COM 		myri10ge_dma_free(&ss->tx.cp[h].dma);
96010253Sxiuyan.wang@Sun.COM 	}
96110253Sxiuyan.wang@Sun.COM 
96210253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*ss->tx.cp) * (ss->tx.mask + 1);
96310253Sxiuyan.wang@Sun.COM 	kmem_free(ss->tx.cp, bytes);
96410253Sxiuyan.wang@Sun.COM 	ss->tx.cp = NULL;
96510253Sxiuyan.wang@Sun.COM 	return (DDI_FAILURE);
96610253Sxiuyan.wang@Sun.COM }
96710253Sxiuyan.wang@Sun.COM 
96810253Sxiuyan.wang@Sun.COM /*
96910253Sxiuyan.wang@Sun.COM  * The eeprom strings on the lanaiX have the format
97010253Sxiuyan.wang@Sun.COM  * SN=x\0
97110253Sxiuyan.wang@Sun.COM  * MAC=x:x:x:x:x:x\0
97210253Sxiuyan.wang@Sun.COM  * PT:ddd mmm xx xx:xx:xx xx\0
97310253Sxiuyan.wang@Sun.COM  * PV:ddd mmm xx xx:xx:xx xx\0
97410253Sxiuyan.wang@Sun.COM  */
97510253Sxiuyan.wang@Sun.COM static int
myri10ge_read_mac_addr(struct myri10ge_priv * mgp)97610253Sxiuyan.wang@Sun.COM myri10ge_read_mac_addr(struct myri10ge_priv *mgp)
97710253Sxiuyan.wang@Sun.COM {
97810253Sxiuyan.wang@Sun.COM #define	MYRI10GE_NEXT_STRING(p) while (ptr < limit && *ptr++)
97910253Sxiuyan.wang@Sun.COM #define	myri10ge_digit(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') :	\
98010253Sxiuyan.wang@Sun.COM 		(((c) >= 'A' && (c) <= 'F') ? (10 + (c) - 'A') :	\
98110253Sxiuyan.wang@Sun.COM 		(((c) >= 'a' && (c) <= 'f') ? (10 + (c) - 'a') : -1)))
98210253Sxiuyan.wang@Sun.COM 
98310253Sxiuyan.wang@Sun.COM 	char *ptr, *limit;
98410253Sxiuyan.wang@Sun.COM 	int i, hv, lv;
98510253Sxiuyan.wang@Sun.COM 
98610253Sxiuyan.wang@Sun.COM 	ptr = mgp->eeprom_strings;
98710253Sxiuyan.wang@Sun.COM 	limit = mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE;
98810253Sxiuyan.wang@Sun.COM 
98910253Sxiuyan.wang@Sun.COM 	while (*ptr != '\0' && ptr < limit) {
99010253Sxiuyan.wang@Sun.COM 		if (memcmp(ptr, "MAC=", 4) == 0) {
99110253Sxiuyan.wang@Sun.COM 			ptr += 4;
99210253Sxiuyan.wang@Sun.COM 			if (myri10ge_verbose)
99310253Sxiuyan.wang@Sun.COM 				printf("%s: mac address = %s\n", mgp->name,
99410253Sxiuyan.wang@Sun.COM 				    ptr);
99510253Sxiuyan.wang@Sun.COM 			mgp->mac_addr_string = ptr;
99610253Sxiuyan.wang@Sun.COM 			for (i = 0; i < 6; i++) {
99710253Sxiuyan.wang@Sun.COM 				if ((ptr + 2) > limit)
99810253Sxiuyan.wang@Sun.COM 					goto abort;
99910253Sxiuyan.wang@Sun.COM 
100010253Sxiuyan.wang@Sun.COM 				if (*(ptr+1) == ':') {
100110253Sxiuyan.wang@Sun.COM 					hv = 0;
100210253Sxiuyan.wang@Sun.COM 					lv = myri10ge_digit(*ptr); ptr++;
100310253Sxiuyan.wang@Sun.COM 				} else {
100410253Sxiuyan.wang@Sun.COM 					hv = myri10ge_digit(*ptr); ptr++;
100510253Sxiuyan.wang@Sun.COM 					lv = myri10ge_digit(*ptr); ptr++;
100610253Sxiuyan.wang@Sun.COM 				}
100710253Sxiuyan.wang@Sun.COM 				mgp->mac_addr[i] = (hv << 4) | lv;
100810253Sxiuyan.wang@Sun.COM 				ptr++;
100910253Sxiuyan.wang@Sun.COM 			}
101010253Sxiuyan.wang@Sun.COM 		}
101110253Sxiuyan.wang@Sun.COM 		if (memcmp((const void *)ptr, "SN=", 3) == 0) {
101210253Sxiuyan.wang@Sun.COM 			ptr += 3;
101310253Sxiuyan.wang@Sun.COM 			mgp->sn_str = (char *)ptr;
101410253Sxiuyan.wang@Sun.COM 		}
101510253Sxiuyan.wang@Sun.COM 		if (memcmp((const void *)ptr, "PC=", 3) == 0) {
101610253Sxiuyan.wang@Sun.COM 			ptr += 3;
101710253Sxiuyan.wang@Sun.COM 			mgp->pc_str = (char *)ptr;
101810253Sxiuyan.wang@Sun.COM 		}
101910253Sxiuyan.wang@Sun.COM 		MYRI10GE_NEXT_STRING(ptr);
102010253Sxiuyan.wang@Sun.COM 	}
102110253Sxiuyan.wang@Sun.COM 
102210253Sxiuyan.wang@Sun.COM 	return (0);
102310253Sxiuyan.wang@Sun.COM 
102410253Sxiuyan.wang@Sun.COM abort:
102510253Sxiuyan.wang@Sun.COM 	cmn_err(CE_WARN, "%s: failed to parse eeprom_strings", mgp->name);
102610253Sxiuyan.wang@Sun.COM 	return (ENXIO);
102710253Sxiuyan.wang@Sun.COM }
102810253Sxiuyan.wang@Sun.COM 
102910253Sxiuyan.wang@Sun.COM 
103010253Sxiuyan.wang@Sun.COM /*
103110253Sxiuyan.wang@Sun.COM  * Determine the register set containing the PCI resource we
103210253Sxiuyan.wang@Sun.COM  * want to map: the memory-mappable part of the interface. We do
103310253Sxiuyan.wang@Sun.COM  * this by scanning the DDI "reg" property of the interface,
103410253Sxiuyan.wang@Sun.COM  * which is an array of mx_ddi_reg_set structures.
103510253Sxiuyan.wang@Sun.COM  */
103610253Sxiuyan.wang@Sun.COM static int
myri10ge_reg_set(dev_info_t * dip,int * reg_set,int * span,unsigned long * busno,unsigned long * devno,unsigned long * funcno)103710253Sxiuyan.wang@Sun.COM myri10ge_reg_set(dev_info_t *dip, int *reg_set, int *span,
103810253Sxiuyan.wang@Sun.COM     unsigned long *busno, unsigned long *devno,
103910253Sxiuyan.wang@Sun.COM     unsigned long *funcno)
104010253Sxiuyan.wang@Sun.COM {
104110253Sxiuyan.wang@Sun.COM 
104210253Sxiuyan.wang@Sun.COM #define	REGISTER_NUMBER(ip)	(ip[0] >>  0 & 0xff)
104310253Sxiuyan.wang@Sun.COM #define	FUNCTION_NUMBER(ip)	(ip[0] >>  8 & 0x07)
104410253Sxiuyan.wang@Sun.COM #define	DEVICE_NUMBER(ip)	(ip[0] >> 11 & 0x1f)
104510253Sxiuyan.wang@Sun.COM #define	BUS_NUMBER(ip)		(ip[0] >> 16 & 0xff)
104610253Sxiuyan.wang@Sun.COM #define	ADDRESS_SPACE(ip)	(ip[0] >> 24 & 0x03)
104710253Sxiuyan.wang@Sun.COM #define	PCI_ADDR_HIGH(ip)	(ip[1])
104810253Sxiuyan.wang@Sun.COM #define	PCI_ADDR_LOW(ip) 	(ip[2])
104910253Sxiuyan.wang@Sun.COM #define	PCI_SPAN_HIGH(ip)	(ip[3])
105010253Sxiuyan.wang@Sun.COM #define	PCI_SPAN_LOW(ip)	(ip[4])
105110253Sxiuyan.wang@Sun.COM 
105210253Sxiuyan.wang@Sun.COM #define	MX_DDI_REG_SET_32_BIT_MEMORY_SPACE 2
105310253Sxiuyan.wang@Sun.COM #define	MX_DDI_REG_SET_64_BIT_MEMORY_SPACE 3
105410253Sxiuyan.wang@Sun.COM 
105510253Sxiuyan.wang@Sun.COM 	int *data, i, *rs;
105610253Sxiuyan.wang@Sun.COM 	uint32_t nelementsp;
105710253Sxiuyan.wang@Sun.COM 
105810253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
105910253Sxiuyan.wang@Sun.COM 	char *address_space_name[] = { "Configuration Space",
106010253Sxiuyan.wang@Sun.COM 					"I/O Space",
106110253Sxiuyan.wang@Sun.COM 					"32-bit Memory Space",
106210253Sxiuyan.wang@Sun.COM 					"64-bit Memory Space"
106310253Sxiuyan.wang@Sun.COM 	};
106410253Sxiuyan.wang@Sun.COM #endif
106510253Sxiuyan.wang@Sun.COM 
106610253Sxiuyan.wang@Sun.COM 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
106710253Sxiuyan.wang@Sun.COM 	    "reg", &data, &nelementsp) != DDI_SUCCESS) {
106810253Sxiuyan.wang@Sun.COM 		printf("Could not determine register set.\n");
106910253Sxiuyan.wang@Sun.COM 		return (ENXIO);
107010253Sxiuyan.wang@Sun.COM 	}
107110253Sxiuyan.wang@Sun.COM 
107210253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
107310253Sxiuyan.wang@Sun.COM 	printf("There are %d register sets.\n", nelementsp / 5);
107410253Sxiuyan.wang@Sun.COM #endif
107510253Sxiuyan.wang@Sun.COM 	if (!nelementsp) {
107610253Sxiuyan.wang@Sun.COM 		printf("Didn't find any \"reg\" properties.\n");
107710253Sxiuyan.wang@Sun.COM 		ddi_prop_free(data);
107810253Sxiuyan.wang@Sun.COM 		return (ENODEV);
107910253Sxiuyan.wang@Sun.COM 	}
108010253Sxiuyan.wang@Sun.COM 
108110253Sxiuyan.wang@Sun.COM 	/* Scan for the register number. */
108210253Sxiuyan.wang@Sun.COM 	rs = &data[0];
108310253Sxiuyan.wang@Sun.COM 	*busno = BUS_NUMBER(rs);
108410253Sxiuyan.wang@Sun.COM 	*devno = DEVICE_NUMBER(rs);
108510253Sxiuyan.wang@Sun.COM 	*funcno = FUNCTION_NUMBER(rs);
108610253Sxiuyan.wang@Sun.COM 
108710253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
108810253Sxiuyan.wang@Sun.COM 	printf("*** Scanning for register number.\n");
108910253Sxiuyan.wang@Sun.COM #endif
109010253Sxiuyan.wang@Sun.COM 	for (i = 0; i < nelementsp / 5; i++) {
109110253Sxiuyan.wang@Sun.COM 		rs = &data[5 * i];
109210253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
109310253Sxiuyan.wang@Sun.COM 		printf("Examining register set %d:\n", i);
109410253Sxiuyan.wang@Sun.COM 		printf("  Register number = %d.\n", REGISTER_NUMBER(rs));
109510253Sxiuyan.wang@Sun.COM 		printf("  Function number = %d.\n", FUNCTION_NUMBER(rs));
109610253Sxiuyan.wang@Sun.COM 		printf("  Device number   = %d.\n", DEVICE_NUMBER(rs));
109710253Sxiuyan.wang@Sun.COM 		printf("  Bus number      = %d.\n", BUS_NUMBER(rs));
109810253Sxiuyan.wang@Sun.COM 		printf("  Address space   = %d (%s ).\n", ADDRESS_SPACE(rs),
109910253Sxiuyan.wang@Sun.COM 		    address_space_name[ADDRESS_SPACE(rs)]);
110010253Sxiuyan.wang@Sun.COM 		printf("  pci address 0x%08x %08x\n", PCI_ADDR_HIGH(rs),
110110253Sxiuyan.wang@Sun.COM 		    PCI_ADDR_LOW(rs));
110210253Sxiuyan.wang@Sun.COM 		printf("  pci span 0x%08x %08x\n", PCI_SPAN_HIGH(rs),
110310253Sxiuyan.wang@Sun.COM 		    PCI_SPAN_LOW(rs));
110410253Sxiuyan.wang@Sun.COM #endif
110510253Sxiuyan.wang@Sun.COM 		/* We are looking for a memory property. */
110610253Sxiuyan.wang@Sun.COM 
110710253Sxiuyan.wang@Sun.COM 		if (ADDRESS_SPACE(rs) == MX_DDI_REG_SET_64_BIT_MEMORY_SPACE ||
110810253Sxiuyan.wang@Sun.COM 		    ADDRESS_SPACE(rs) == MX_DDI_REG_SET_32_BIT_MEMORY_SPACE) {
110910253Sxiuyan.wang@Sun.COM 			*reg_set = i;
111010253Sxiuyan.wang@Sun.COM 
111110253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
111210253Sxiuyan.wang@Sun.COM 			printf("%s uses register set %d.\n",
111310253Sxiuyan.wang@Sun.COM 			    address_space_name[ADDRESS_SPACE(rs)], *reg_set);
111410253Sxiuyan.wang@Sun.COM #endif
111510253Sxiuyan.wang@Sun.COM 
111610253Sxiuyan.wang@Sun.COM 			*span = (PCI_SPAN_LOW(rs));
111710253Sxiuyan.wang@Sun.COM #ifdef MYRI10GE_REGSET_VERBOSE
111810253Sxiuyan.wang@Sun.COM 			printf("Board span is 0x%x\n", *span);
111910253Sxiuyan.wang@Sun.COM #endif
112010253Sxiuyan.wang@Sun.COM 			break;
112110253Sxiuyan.wang@Sun.COM 		}
112210253Sxiuyan.wang@Sun.COM 	}
112310253Sxiuyan.wang@Sun.COM 
112410253Sxiuyan.wang@Sun.COM 	ddi_prop_free(data);
112510253Sxiuyan.wang@Sun.COM 
112610253Sxiuyan.wang@Sun.COM 	/* If no match, fail. */
112710253Sxiuyan.wang@Sun.COM 	if (i >= nelementsp / 5) {
112810253Sxiuyan.wang@Sun.COM 		return (EIO);
112910253Sxiuyan.wang@Sun.COM 	}
113010253Sxiuyan.wang@Sun.COM 
113110253Sxiuyan.wang@Sun.COM 	return (0);
113210253Sxiuyan.wang@Sun.COM }
113310253Sxiuyan.wang@Sun.COM 
113410253Sxiuyan.wang@Sun.COM 
113510253Sxiuyan.wang@Sun.COM static int
myri10ge_load_firmware_from_zlib(struct myri10ge_priv * mgp,uint32_t * limit)113610253Sxiuyan.wang@Sun.COM myri10ge_load_firmware_from_zlib(struct myri10ge_priv *mgp, uint32_t *limit)
113710253Sxiuyan.wang@Sun.COM {
113810253Sxiuyan.wang@Sun.COM 	void *inflate_buffer;
113910253Sxiuyan.wang@Sun.COM 	int rv, status;
114010253Sxiuyan.wang@Sun.COM 	size_t sram_size = mgp->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE;
114110253Sxiuyan.wang@Sun.COM 	size_t destlen;
114210253Sxiuyan.wang@Sun.COM 	mcp_gen_header_t *hdr;
114310253Sxiuyan.wang@Sun.COM 	unsigned hdr_offset, i;
114410253Sxiuyan.wang@Sun.COM 
114510253Sxiuyan.wang@Sun.COM 
114610253Sxiuyan.wang@Sun.COM 	*limit = 0; /* -Wuninitialized */
114710253Sxiuyan.wang@Sun.COM 	status = 0;
114810253Sxiuyan.wang@Sun.COM 
114910253Sxiuyan.wang@Sun.COM 	inflate_buffer = kmem_zalloc(sram_size, KM_NOSLEEP);
115010253Sxiuyan.wang@Sun.COM 	if (!inflate_buffer) {
115110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
115210253Sxiuyan.wang@Sun.COM 		    "%s: Could not allocate buffer to inflate mcp\n",
115310253Sxiuyan.wang@Sun.COM 		    mgp->name);
115410253Sxiuyan.wang@Sun.COM 		return (ENOMEM);
115510253Sxiuyan.wang@Sun.COM 	}
115610253Sxiuyan.wang@Sun.COM 
115710253Sxiuyan.wang@Sun.COM 	destlen = sram_size;
115810253Sxiuyan.wang@Sun.COM 	rv = z_uncompress(inflate_buffer, &destlen, mgp->eth_z8e,
115910253Sxiuyan.wang@Sun.COM 	    mgp->eth_z8e_length);
116010253Sxiuyan.wang@Sun.COM 
116110253Sxiuyan.wang@Sun.COM 	if (rv != Z_OK) {
116210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Could not inflate mcp: %s\n",
116310253Sxiuyan.wang@Sun.COM 		    mgp->name, z_strerror(rv));
116410253Sxiuyan.wang@Sun.COM 		status = ENXIO;
116510253Sxiuyan.wang@Sun.COM 		goto abort;
116610253Sxiuyan.wang@Sun.COM 	}
116710253Sxiuyan.wang@Sun.COM 
116810253Sxiuyan.wang@Sun.COM 	*limit = (uint32_t)destlen;
116910253Sxiuyan.wang@Sun.COM 
117010253Sxiuyan.wang@Sun.COM 	hdr_offset = htonl(*(uint32_t *)(void *)((char *)inflate_buffer +
117110253Sxiuyan.wang@Sun.COM 	    MCP_HEADER_PTR_OFFSET));
117210253Sxiuyan.wang@Sun.COM 	hdr = (void *)((char *)inflate_buffer + hdr_offset);
117310253Sxiuyan.wang@Sun.COM 	if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) {
117410253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Bad firmware type: 0x%x\n", mgp->name,
117510253Sxiuyan.wang@Sun.COM 		    ntohl(hdr->mcp_type));
117610253Sxiuyan.wang@Sun.COM 		status = EIO;
117710253Sxiuyan.wang@Sun.COM 		goto abort;
117810253Sxiuyan.wang@Sun.COM 	}
117910253Sxiuyan.wang@Sun.COM 
118010253Sxiuyan.wang@Sun.COM 	/* save firmware version for kstat */
118110253Sxiuyan.wang@Sun.COM 	(void) strncpy(mgp->fw_version, hdr->version, sizeof (mgp->fw_version));
118210253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
118310253Sxiuyan.wang@Sun.COM 		printf("%s: firmware id: %s\n", mgp->name, hdr->version);
118410253Sxiuyan.wang@Sun.COM 
118510253Sxiuyan.wang@Sun.COM 	/* Copy the inflated firmware to NIC SRAM. */
118610253Sxiuyan.wang@Sun.COM 	for (i = 0; i < *limit; i += 256) {
118710253Sxiuyan.wang@Sun.COM 		myri10ge_pio_copy((char *)mgp->sram + MYRI10GE_FW_OFFSET + i,
118810253Sxiuyan.wang@Sun.COM 		    (char *)inflate_buffer + i,
118910253Sxiuyan.wang@Sun.COM 		    min(256U, (unsigned)(*limit - i)));
119010253Sxiuyan.wang@Sun.COM 		mb();
119110253Sxiuyan.wang@Sun.COM 		(void) *(int *)(void *)mgp->sram;
119210253Sxiuyan.wang@Sun.COM 		mb();
119310253Sxiuyan.wang@Sun.COM 	}
119410253Sxiuyan.wang@Sun.COM 
119510253Sxiuyan.wang@Sun.COM abort:
119610253Sxiuyan.wang@Sun.COM 	kmem_free(inflate_buffer, sram_size);
119710253Sxiuyan.wang@Sun.COM 
119810253Sxiuyan.wang@Sun.COM 	return (status);
119910253Sxiuyan.wang@Sun.COM 
120010253Sxiuyan.wang@Sun.COM }
120110253Sxiuyan.wang@Sun.COM 
120210253Sxiuyan.wang@Sun.COM 
120310253Sxiuyan.wang@Sun.COM int
myri10ge_send_cmd(struct myri10ge_priv * mgp,uint32_t cmd,myri10ge_cmd_t * data)120410253Sxiuyan.wang@Sun.COM myri10ge_send_cmd(struct myri10ge_priv *mgp, uint32_t cmd,
120510253Sxiuyan.wang@Sun.COM 		myri10ge_cmd_t *data)
120610253Sxiuyan.wang@Sun.COM {
120710253Sxiuyan.wang@Sun.COM 	mcp_cmd_t *buf;
120810253Sxiuyan.wang@Sun.COM 	char buf_bytes[sizeof (*buf) + 8];
120910253Sxiuyan.wang@Sun.COM 	volatile mcp_cmd_response_t *response = mgp->cmd;
121010253Sxiuyan.wang@Sun.COM 	volatile char *cmd_addr =
121110253Sxiuyan.wang@Sun.COM 	    (volatile char *)mgp->sram + MXGEFW_ETH_CMD;
121210253Sxiuyan.wang@Sun.COM 	int sleep_total = 0;
121310253Sxiuyan.wang@Sun.COM 
121410253Sxiuyan.wang@Sun.COM 	/* ensure buf is aligned to 8 bytes */
121510253Sxiuyan.wang@Sun.COM 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
121610253Sxiuyan.wang@Sun.COM 
121710253Sxiuyan.wang@Sun.COM 	buf->data0 = htonl(data->data0);
121810253Sxiuyan.wang@Sun.COM 	buf->data1 = htonl(data->data1);
121910253Sxiuyan.wang@Sun.COM 	buf->data2 = htonl(data->data2);
122010253Sxiuyan.wang@Sun.COM 	buf->cmd = htonl(cmd);
122110253Sxiuyan.wang@Sun.COM 	buf->response_addr.low = mgp->cmd_dma.low;
122210253Sxiuyan.wang@Sun.COM 	buf->response_addr.high = mgp->cmd_dma.high;
122310253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->cmd_lock);
122410253Sxiuyan.wang@Sun.COM 	response->result = 0xffffffff;
122510253Sxiuyan.wang@Sun.COM 	mb();
122610253Sxiuyan.wang@Sun.COM 
122710253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy((void *)cmd_addr, buf, sizeof (*buf));
122810253Sxiuyan.wang@Sun.COM 
122910253Sxiuyan.wang@Sun.COM 	/* wait up to 20ms */
123010253Sxiuyan.wang@Sun.COM 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
123110253Sxiuyan.wang@Sun.COM 		mb();
123210253Sxiuyan.wang@Sun.COM 		if (response->result != 0xffffffff) {
123310253Sxiuyan.wang@Sun.COM 			if (response->result == 0) {
123410253Sxiuyan.wang@Sun.COM 				data->data0 = ntohl(response->data);
123510253Sxiuyan.wang@Sun.COM 				mutex_exit(&mgp->cmd_lock);
123610253Sxiuyan.wang@Sun.COM 				return (0);
123710253Sxiuyan.wang@Sun.COM 			} else if (ntohl(response->result)
123810253Sxiuyan.wang@Sun.COM 			    == MXGEFW_CMD_UNKNOWN) {
123910253Sxiuyan.wang@Sun.COM 				mutex_exit(&mgp->cmd_lock);
124010253Sxiuyan.wang@Sun.COM 				return (ENOSYS);
124110253Sxiuyan.wang@Sun.COM 			} else if (ntohl(response->result)
124210253Sxiuyan.wang@Sun.COM 			    == MXGEFW_CMD_ERROR_UNALIGNED) {
124310253Sxiuyan.wang@Sun.COM 				mutex_exit(&mgp->cmd_lock);
124410253Sxiuyan.wang@Sun.COM 				return (E2BIG);
124510253Sxiuyan.wang@Sun.COM 			} else {
124610253Sxiuyan.wang@Sun.COM 				cmn_err(CE_WARN,
124710253Sxiuyan.wang@Sun.COM 				    "%s: command %d failed, result = %d\n",
124810253Sxiuyan.wang@Sun.COM 				    mgp->name, cmd, ntohl(response->result));
124910253Sxiuyan.wang@Sun.COM 				mutex_exit(&mgp->cmd_lock);
125010253Sxiuyan.wang@Sun.COM 				return (ENXIO);
125110253Sxiuyan.wang@Sun.COM 			}
125210253Sxiuyan.wang@Sun.COM 		}
125310253Sxiuyan.wang@Sun.COM 		drv_usecwait(1000);
125410253Sxiuyan.wang@Sun.COM 	}
125510253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->cmd_lock);
125610253Sxiuyan.wang@Sun.COM 	cmn_err(CE_WARN, "%s: command %d timed out, result = %d\n",
125710253Sxiuyan.wang@Sun.COM 	    mgp->name, cmd, ntohl(response->result));
125810253Sxiuyan.wang@Sun.COM 	return (EAGAIN);
125910253Sxiuyan.wang@Sun.COM }
126010253Sxiuyan.wang@Sun.COM 
126110253Sxiuyan.wang@Sun.COM /*
126210253Sxiuyan.wang@Sun.COM  * Enable or disable periodic RDMAs from the host to make certain
126310253Sxiuyan.wang@Sun.COM  * chipsets resend dropped PCIe messages
126410253Sxiuyan.wang@Sun.COM  */
126510253Sxiuyan.wang@Sun.COM 
126610253Sxiuyan.wang@Sun.COM static void
myri10ge_dummy_rdma(struct myri10ge_priv * mgp,int enable)126710253Sxiuyan.wang@Sun.COM myri10ge_dummy_rdma(struct myri10ge_priv *mgp, int enable)
126810253Sxiuyan.wang@Sun.COM {
126910253Sxiuyan.wang@Sun.COM 	char buf_bytes[72];
127010253Sxiuyan.wang@Sun.COM 	volatile uint32_t *confirm;
127110253Sxiuyan.wang@Sun.COM 	volatile char *submit;
127210253Sxiuyan.wang@Sun.COM 	uint32_t *buf;
127310253Sxiuyan.wang@Sun.COM 	int i;
127410253Sxiuyan.wang@Sun.COM 
127510253Sxiuyan.wang@Sun.COM 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
127610253Sxiuyan.wang@Sun.COM 
127710253Sxiuyan.wang@Sun.COM 	/* clear confirmation addr */
127810253Sxiuyan.wang@Sun.COM 	confirm = (volatile uint32_t *)mgp->cmd;
127910253Sxiuyan.wang@Sun.COM 	*confirm = 0;
128010253Sxiuyan.wang@Sun.COM 	mb();
128110253Sxiuyan.wang@Sun.COM 
128210253Sxiuyan.wang@Sun.COM 	/*
128310253Sxiuyan.wang@Sun.COM 	 * send an rdma command to the PCIe engine, and wait for the
128410253Sxiuyan.wang@Sun.COM 	 * response in the confirmation address.  The firmware should
128510253Sxiuyan.wang@Sun.COM 	 *  write a -1 there to indicate it is alive and well
128610253Sxiuyan.wang@Sun.COM 	 */
128710253Sxiuyan.wang@Sun.COM 
128810253Sxiuyan.wang@Sun.COM 	buf[0] = mgp->cmd_dma.high;		/* confirm addr MSW */
128910253Sxiuyan.wang@Sun.COM 	buf[1] = mgp->cmd_dma.low;		/* confirm addr LSW */
129010253Sxiuyan.wang@Sun.COM 	buf[2] = htonl(0xffffffff);		/* confirm data */
129110253Sxiuyan.wang@Sun.COM 	buf[3] = htonl(mgp->cmd_dma.high); 	/* dummy addr MSW */
129210253Sxiuyan.wang@Sun.COM 	buf[4] = htonl(mgp->cmd_dma.low); 	/* dummy addr LSW */
129310253Sxiuyan.wang@Sun.COM 	buf[5] = htonl(enable);			/* enable? */
129410253Sxiuyan.wang@Sun.COM 
129510253Sxiuyan.wang@Sun.COM 
129610253Sxiuyan.wang@Sun.COM 	submit = (volatile char *)(mgp->sram + MXGEFW_BOOT_DUMMY_RDMA);
129710253Sxiuyan.wang@Sun.COM 
129810253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy((char *)submit, buf, 64);
129910253Sxiuyan.wang@Sun.COM 	mb();
130010253Sxiuyan.wang@Sun.COM 	drv_usecwait(1000);
130110253Sxiuyan.wang@Sun.COM 	mb();
130210253Sxiuyan.wang@Sun.COM 	i = 0;
130310253Sxiuyan.wang@Sun.COM 	while (*confirm != 0xffffffff && i < 20) {
130410253Sxiuyan.wang@Sun.COM 		drv_usecwait(1000);
130510253Sxiuyan.wang@Sun.COM 		i++;
130610253Sxiuyan.wang@Sun.COM 	}
130710253Sxiuyan.wang@Sun.COM 	if (*confirm != 0xffffffff) {
130810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: dummy rdma %s failed (%p = 0x%x)",
130910253Sxiuyan.wang@Sun.COM 		    mgp->name,
131010253Sxiuyan.wang@Sun.COM 		    (enable ? "enable" : "disable"), (void*) confirm, *confirm);
131110253Sxiuyan.wang@Sun.COM 	}
131210253Sxiuyan.wang@Sun.COM }
131310253Sxiuyan.wang@Sun.COM 
131410253Sxiuyan.wang@Sun.COM static int
myri10ge_load_firmware(struct myri10ge_priv * mgp)131510253Sxiuyan.wang@Sun.COM myri10ge_load_firmware(struct myri10ge_priv *mgp)
131610253Sxiuyan.wang@Sun.COM {
131710253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
131810253Sxiuyan.wang@Sun.COM 	volatile uint32_t *confirm;
131910253Sxiuyan.wang@Sun.COM 	volatile char *submit;
132010253Sxiuyan.wang@Sun.COM 	char buf_bytes[72];
132110253Sxiuyan.wang@Sun.COM 	uint32_t *buf, size;
132210253Sxiuyan.wang@Sun.COM 	int status, i;
132310253Sxiuyan.wang@Sun.COM 
132410253Sxiuyan.wang@Sun.COM 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
132510253Sxiuyan.wang@Sun.COM 
132610253Sxiuyan.wang@Sun.COM 	status = myri10ge_load_firmware_from_zlib(mgp, &size);
132710253Sxiuyan.wang@Sun.COM 	if (status) {
132810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: firmware loading failed\n", mgp->name);
132910253Sxiuyan.wang@Sun.COM 		return (status);
133010253Sxiuyan.wang@Sun.COM 	}
133110253Sxiuyan.wang@Sun.COM 
133210253Sxiuyan.wang@Sun.COM 	/* clear confirmation addr */
133310253Sxiuyan.wang@Sun.COM 	confirm = (volatile uint32_t *)mgp->cmd;
133410253Sxiuyan.wang@Sun.COM 	*confirm = 0;
133510253Sxiuyan.wang@Sun.COM 	mb();
133610253Sxiuyan.wang@Sun.COM 
133710253Sxiuyan.wang@Sun.COM 	/*
133810253Sxiuyan.wang@Sun.COM 	 * send a reload command to the bootstrap MCP, and wait for the
133910253Sxiuyan.wang@Sun.COM 	 * response in the confirmation address.  The firmware should
134010253Sxiuyan.wang@Sun.COM 	 * write a -1 there to indicate it is alive and well
134110253Sxiuyan.wang@Sun.COM 	 */
134210253Sxiuyan.wang@Sun.COM 
134310253Sxiuyan.wang@Sun.COM 	buf[0] = mgp->cmd_dma.high;	/* confirm addr MSW */
134410253Sxiuyan.wang@Sun.COM 	buf[1] = mgp->cmd_dma.low;	/* confirm addr LSW */
134510253Sxiuyan.wang@Sun.COM 	buf[2] = htonl(0xffffffff);	/* confirm data */
134610253Sxiuyan.wang@Sun.COM 
134710253Sxiuyan.wang@Sun.COM 	/*
134810253Sxiuyan.wang@Sun.COM 	 * FIX: All newest firmware should un-protect the bottom of
134910253Sxiuyan.wang@Sun.COM 	 * the sram before handoff. However, the very first interfaces
135010253Sxiuyan.wang@Sun.COM 	 * do not. Therefore the handoff copy must skip the first 8 bytes
135110253Sxiuyan.wang@Sun.COM 	 */
135210253Sxiuyan.wang@Sun.COM 	buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */
135310253Sxiuyan.wang@Sun.COM 	buf[4] = htonl(size - 8); 	/* length of code */
135410253Sxiuyan.wang@Sun.COM 	buf[5] = htonl(8);		/* where to copy to */
135510253Sxiuyan.wang@Sun.COM 	buf[6] = htonl(0);		/* where to jump to */
135610253Sxiuyan.wang@Sun.COM 
135710253Sxiuyan.wang@Sun.COM 	submit = (volatile char *)(mgp->sram + MXGEFW_BOOT_HANDOFF);
135810253Sxiuyan.wang@Sun.COM 
135910253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy((char *)submit, buf, 64);
136010253Sxiuyan.wang@Sun.COM 	mb();
136110253Sxiuyan.wang@Sun.COM 	drv_usecwait(1000);
136210253Sxiuyan.wang@Sun.COM 	mb();
136310253Sxiuyan.wang@Sun.COM 	i = 0;
136410253Sxiuyan.wang@Sun.COM 	while (*confirm != 0xffffffff && i < 1000) {
136510253Sxiuyan.wang@Sun.COM 		drv_usecwait(1000);
136610253Sxiuyan.wang@Sun.COM 		i++;
136710253Sxiuyan.wang@Sun.COM 	}
136810253Sxiuyan.wang@Sun.COM 	if (*confirm != 0xffffffff) {
136910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: handoff failed (%p = 0x%x)",
137010253Sxiuyan.wang@Sun.COM 		    mgp->name, (void *) confirm, *confirm);
137110253Sxiuyan.wang@Sun.COM 
137210253Sxiuyan.wang@Sun.COM 		return (ENXIO);
137310253Sxiuyan.wang@Sun.COM 	}
137410253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
137510253Sxiuyan.wang@Sun.COM 	if (status != 0) {
137610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed MXGEFW_CMD_GET_RX_RING_SIZE\n",
137710253Sxiuyan.wang@Sun.COM 		    mgp->name);
137810253Sxiuyan.wang@Sun.COM 		return (ENXIO);
137910253Sxiuyan.wang@Sun.COM 	}
138010253Sxiuyan.wang@Sun.COM 
138110253Sxiuyan.wang@Sun.COM 	mgp->max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
138210253Sxiuyan.wang@Sun.COM 	myri10ge_dummy_rdma(mgp, 1);
138310253Sxiuyan.wang@Sun.COM 	return (0);
138410253Sxiuyan.wang@Sun.COM }
138510253Sxiuyan.wang@Sun.COM 
138610253Sxiuyan.wang@Sun.COM static int
myri10ge_m_unicst(void * arg,const uint8_t * addr)138710253Sxiuyan.wang@Sun.COM myri10ge_m_unicst(void *arg, const uint8_t *addr)
138810253Sxiuyan.wang@Sun.COM {
138910253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
139010253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
139110253Sxiuyan.wang@Sun.COM 	int status;
139210253Sxiuyan.wang@Sun.COM 
139310253Sxiuyan.wang@Sun.COM 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
139410253Sxiuyan.wang@Sun.COM 	    | (addr[2] << 8) | addr[3]);
139510253Sxiuyan.wang@Sun.COM 
139610253Sxiuyan.wang@Sun.COM 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
139710253Sxiuyan.wang@Sun.COM 
139810253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd);
139910253Sxiuyan.wang@Sun.COM 	if (status == 0 && (addr != mgp->mac_addr))
140010253Sxiuyan.wang@Sun.COM 		(void) memcpy(mgp->mac_addr, addr, sizeof (mgp->mac_addr));
140110253Sxiuyan.wang@Sun.COM 
140210253Sxiuyan.wang@Sun.COM 	return (status);
140310253Sxiuyan.wang@Sun.COM }
140410253Sxiuyan.wang@Sun.COM 
140510253Sxiuyan.wang@Sun.COM static int
myri10ge_change_pause(struct myri10ge_priv * mgp,int pause)140610253Sxiuyan.wang@Sun.COM myri10ge_change_pause(struct myri10ge_priv *mgp, int pause)
140710253Sxiuyan.wang@Sun.COM {
140810253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
140910253Sxiuyan.wang@Sun.COM 	int status;
141010253Sxiuyan.wang@Sun.COM 
141110253Sxiuyan.wang@Sun.COM 	if (pause)
141210253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_ENABLE_FLOW_CONTROL,
141310253Sxiuyan.wang@Sun.COM 		    &cmd);
141410253Sxiuyan.wang@Sun.COM 	else
141510253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_DISABLE_FLOW_CONTROL,
141610253Sxiuyan.wang@Sun.COM 		    &cmd);
141710253Sxiuyan.wang@Sun.COM 
141810253Sxiuyan.wang@Sun.COM 	if (status) {
141910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Failed to set flow control mode\n",
142010253Sxiuyan.wang@Sun.COM 		    mgp->name);
142110253Sxiuyan.wang@Sun.COM 		return (ENXIO);
142210253Sxiuyan.wang@Sun.COM 	}
142310253Sxiuyan.wang@Sun.COM 	mgp->pause = pause;
142410253Sxiuyan.wang@Sun.COM 	return (0);
142510253Sxiuyan.wang@Sun.COM }
142610253Sxiuyan.wang@Sun.COM 
142710253Sxiuyan.wang@Sun.COM static void
myri10ge_change_promisc(struct myri10ge_priv * mgp,int promisc)142810253Sxiuyan.wang@Sun.COM myri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc)
142910253Sxiuyan.wang@Sun.COM {
143010253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
143110253Sxiuyan.wang@Sun.COM 	int status;
143210253Sxiuyan.wang@Sun.COM 
143310253Sxiuyan.wang@Sun.COM 	if (promisc)
143410253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_ENABLE_PROMISC, &cmd);
143510253Sxiuyan.wang@Sun.COM 	else
143610253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_DISABLE_PROMISC, &cmd);
143710253Sxiuyan.wang@Sun.COM 
143810253Sxiuyan.wang@Sun.COM 	if (status) {
143910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Failed to set promisc mode\n",
144010253Sxiuyan.wang@Sun.COM 		    mgp->name);
144110253Sxiuyan.wang@Sun.COM 	}
144210253Sxiuyan.wang@Sun.COM }
144310253Sxiuyan.wang@Sun.COM 
144410253Sxiuyan.wang@Sun.COM static int
myri10ge_dma_test(struct myri10ge_priv * mgp,int test_type)144510253Sxiuyan.wang@Sun.COM myri10ge_dma_test(struct myri10ge_priv *mgp, int test_type)
144610253Sxiuyan.wang@Sun.COM {
144710253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
144810253Sxiuyan.wang@Sun.COM 	int status;
144910253Sxiuyan.wang@Sun.COM 	uint32_t len;
145010253Sxiuyan.wang@Sun.COM 	void *dmabench;
145110253Sxiuyan.wang@Sun.COM 	struct myri10ge_dma_stuff dmabench_dma;
145210253Sxiuyan.wang@Sun.COM 	char *test = " ";
145310253Sxiuyan.wang@Sun.COM 
145410253Sxiuyan.wang@Sun.COM 	/*
145510253Sxiuyan.wang@Sun.COM 	 * Run a small DMA test.
145610253Sxiuyan.wang@Sun.COM 	 * The magic multipliers to the length tell the firmware
145710253Sxiuyan.wang@Sun.COM 	 * tp do DMA read, write, or read+write tests.  The
145810253Sxiuyan.wang@Sun.COM 	 * results are returned in cmd.data0.  The upper 16
145910253Sxiuyan.wang@Sun.COM 	 * bits or the return is the number of transfers completed.
146010253Sxiuyan.wang@Sun.COM 	 * The lower 16 bits is the time in 0.5us ticks that the
146110253Sxiuyan.wang@Sun.COM 	 * transfers took to complete
146210253Sxiuyan.wang@Sun.COM 	 */
146310253Sxiuyan.wang@Sun.COM 
146410253Sxiuyan.wang@Sun.COM 	len = mgp->tx_boundary;
146510253Sxiuyan.wang@Sun.COM 
146610253Sxiuyan.wang@Sun.COM 	dmabench = myri10ge_dma_alloc(mgp->dip, len,
146710253Sxiuyan.wang@Sun.COM 	    &myri10ge_rx_jumbo_dma_attr, &myri10ge_dev_access_attr,
146810253Sxiuyan.wang@Sun.COM 	    DDI_DMA_STREAMING,  DDI_DMA_RDWR|DDI_DMA_STREAMING,
146910253Sxiuyan.wang@Sun.COM 	    &dmabench_dma, 1, DDI_DMA_DONTWAIT);
147010253Sxiuyan.wang@Sun.COM 	mgp->read_dma = mgp->write_dma = mgp->read_write_dma = 0;
147110253Sxiuyan.wang@Sun.COM 	if (dmabench == NULL) {
147210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s dma benchmark aborted\n", mgp->name);
147310253Sxiuyan.wang@Sun.COM 		return (ENOMEM);
147410253Sxiuyan.wang@Sun.COM 	}
147510253Sxiuyan.wang@Sun.COM 
147610253Sxiuyan.wang@Sun.COM 	cmd.data0 = ntohl(dmabench_dma.low);
147710253Sxiuyan.wang@Sun.COM 	cmd.data1 = ntohl(dmabench_dma.high);
147810253Sxiuyan.wang@Sun.COM 	cmd.data2 = len * 0x10000;
147910253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, test_type, &cmd);
148010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
148110253Sxiuyan.wang@Sun.COM 		test = "read";
148210253Sxiuyan.wang@Sun.COM 		goto abort;
148310253Sxiuyan.wang@Sun.COM 	}
148410253Sxiuyan.wang@Sun.COM 	mgp->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
148510253Sxiuyan.wang@Sun.COM 
148610253Sxiuyan.wang@Sun.COM 	cmd.data0 = ntohl(dmabench_dma.low);
148710253Sxiuyan.wang@Sun.COM 	cmd.data1 = ntohl(dmabench_dma.high);
148810253Sxiuyan.wang@Sun.COM 	cmd.data2 = len * 0x1;
148910253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, test_type, &cmd);
149010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
149110253Sxiuyan.wang@Sun.COM 		test = "write";
149210253Sxiuyan.wang@Sun.COM 		goto abort;
149310253Sxiuyan.wang@Sun.COM 	}
149410253Sxiuyan.wang@Sun.COM 	mgp->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
149510253Sxiuyan.wang@Sun.COM 
149610253Sxiuyan.wang@Sun.COM 	cmd.data0 = ntohl(dmabench_dma.low);
149710253Sxiuyan.wang@Sun.COM 	cmd.data1 = ntohl(dmabench_dma.high);
149810253Sxiuyan.wang@Sun.COM 	cmd.data2 = len * 0x10001;
149910253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, test_type, &cmd);
150010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
150110253Sxiuyan.wang@Sun.COM 		test = "read/write";
150210253Sxiuyan.wang@Sun.COM 		goto abort;
150310253Sxiuyan.wang@Sun.COM 	}
150410253Sxiuyan.wang@Sun.COM 	mgp->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
150510253Sxiuyan.wang@Sun.COM 	    (cmd.data0 & 0xffff);
150610253Sxiuyan.wang@Sun.COM 
150710253Sxiuyan.wang@Sun.COM 
150810253Sxiuyan.wang@Sun.COM abort:
150910253Sxiuyan.wang@Sun.COM 	myri10ge_dma_free(&dmabench_dma);
151010253Sxiuyan.wang@Sun.COM 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
151110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s %s dma benchmark failed\n", mgp->name,
151210253Sxiuyan.wang@Sun.COM 		    test);
151310253Sxiuyan.wang@Sun.COM 	return (status);
151410253Sxiuyan.wang@Sun.COM }
151510253Sxiuyan.wang@Sun.COM 
151610253Sxiuyan.wang@Sun.COM static int
myri10ge_reset(struct myri10ge_priv * mgp)151710253Sxiuyan.wang@Sun.COM myri10ge_reset(struct myri10ge_priv *mgp)
151810253Sxiuyan.wang@Sun.COM {
151910253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
152010253Sxiuyan.wang@Sun.COM 	struct myri10ge_nic_stat *ethstat;
152110253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
152210253Sxiuyan.wang@Sun.COM 	int i, status;
152310253Sxiuyan.wang@Sun.COM 	size_t bytes;
152410253Sxiuyan.wang@Sun.COM 
152510253Sxiuyan.wang@Sun.COM 	/* send a reset command to the card to see if it is alive */
152610253Sxiuyan.wang@Sun.COM 	(void) memset(&cmd, 0, sizeof (cmd));
152710253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd);
152810253Sxiuyan.wang@Sun.COM 	if (status != 0) {
152910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed reset\n", mgp->name);
153010253Sxiuyan.wang@Sun.COM 		return (ENXIO);
153110253Sxiuyan.wang@Sun.COM 	}
153210253Sxiuyan.wang@Sun.COM 
153310253Sxiuyan.wang@Sun.COM 	/* Now exchange information about interrupts  */
153410253Sxiuyan.wang@Sun.COM 
153510253Sxiuyan.wang@Sun.COM 	bytes = mgp->max_intr_slots * sizeof (*mgp->ss[0].rx_done.entry);
153610253Sxiuyan.wang@Sun.COM 	cmd.data0 = (uint32_t)bytes;
153710253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
153810253Sxiuyan.wang@Sun.COM 
153910253Sxiuyan.wang@Sun.COM 	/*
154010253Sxiuyan.wang@Sun.COM 	 * Even though we already know how many slices are supported
154110253Sxiuyan.wang@Sun.COM 	 * via myri10ge_probe_slices() MXGEFW_CMD_GET_MAX_RSS_QUEUES
154210253Sxiuyan.wang@Sun.COM 	 * has magic side effects, and must be called after a reset.
154310253Sxiuyan.wang@Sun.COM 	 * It must be called prior to calling any RSS related cmds,
154410253Sxiuyan.wang@Sun.COM 	 * including assigning an interrupt queue for anything but
154510253Sxiuyan.wang@Sun.COM 	 * slice 0.  It must also be called *after*
154610253Sxiuyan.wang@Sun.COM 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
154710253Sxiuyan.wang@Sun.COM 	 * the firmware to compute offsets.
154810253Sxiuyan.wang@Sun.COM 	 */
154910253Sxiuyan.wang@Sun.COM 
155010253Sxiuyan.wang@Sun.COM 	if (mgp->num_slices > 1) {
155110253Sxiuyan.wang@Sun.COM 
155210253Sxiuyan.wang@Sun.COM 		/* ask the maximum number of slices it supports */
155310253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
155410253Sxiuyan.wang@Sun.COM 		    &cmd);
155510253Sxiuyan.wang@Sun.COM 		if (status != 0) {
155610253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
155710253Sxiuyan.wang@Sun.COM 			    "%s: failed to get number of slices\n",
155810253Sxiuyan.wang@Sun.COM 			    mgp->name);
155910253Sxiuyan.wang@Sun.COM 			return (status);
156010253Sxiuyan.wang@Sun.COM 		}
156110253Sxiuyan.wang@Sun.COM 
156210253Sxiuyan.wang@Sun.COM 		/*
156310253Sxiuyan.wang@Sun.COM 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
156410253Sxiuyan.wang@Sun.COM 		 * to setting up the interrupt queue DMA
156510253Sxiuyan.wang@Sun.COM 		 */
156610253Sxiuyan.wang@Sun.COM 
156710253Sxiuyan.wang@Sun.COM 		cmd.data0 = mgp->num_slices;
156810253Sxiuyan.wang@Sun.COM 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE |
156910253Sxiuyan.wang@Sun.COM 		    MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
157010253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
157110253Sxiuyan.wang@Sun.COM 		    &cmd);
157210253Sxiuyan.wang@Sun.COM 		if (status != 0) {
157310253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
157410253Sxiuyan.wang@Sun.COM 			    "%s: failed to set number of slices\n",
157510253Sxiuyan.wang@Sun.COM 			    mgp->name);
157610253Sxiuyan.wang@Sun.COM 			return (status);
157710253Sxiuyan.wang@Sun.COM 		}
157810253Sxiuyan.wang@Sun.COM 	}
157910253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
158010253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
158110253Sxiuyan.wang@Sun.COM 		cmd.data0 = ntohl(ss->rx_done.dma.low);
158210253Sxiuyan.wang@Sun.COM 		cmd.data1 = ntohl(ss->rx_done.dma.high);
158310253Sxiuyan.wang@Sun.COM 		cmd.data2 = i;
158410253Sxiuyan.wang@Sun.COM 		status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA,
158510253Sxiuyan.wang@Sun.COM 		    &cmd);
158610253Sxiuyan.wang@Sun.COM 	};
158710253Sxiuyan.wang@Sun.COM 
158810253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp,  MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
158910253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
159010253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
159110253Sxiuyan.wang@Sun.COM 		ss->irq_claim = (volatile unsigned int *)
159210253Sxiuyan.wang@Sun.COM 		    (void *)(mgp->sram + cmd.data0 + 8 * i);
159310253Sxiuyan.wang@Sun.COM 	}
159410253Sxiuyan.wang@Sun.COM 
159510253Sxiuyan.wang@Sun.COM 	if (mgp->ddi_intr_type == DDI_INTR_TYPE_FIXED) {
159610253Sxiuyan.wang@Sun.COM 		status |= myri10ge_send_cmd(mgp,
159710253Sxiuyan.wang@Sun.COM 		    MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
159810253Sxiuyan.wang@Sun.COM 		mgp->irq_deassert = (uint32_t *)(void *)(mgp->sram + cmd.data0);
159910253Sxiuyan.wang@Sun.COM 	}
160010253Sxiuyan.wang@Sun.COM 
160110253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp,
160210253Sxiuyan.wang@Sun.COM 	    MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
160310253Sxiuyan.wang@Sun.COM 	mgp->intr_coal_delay_ptr = (uint32_t *)(void *)(mgp->sram + cmd.data0);
160410253Sxiuyan.wang@Sun.COM 
160510253Sxiuyan.wang@Sun.COM 	if (status != 0) {
160610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed set interrupt parameters\n",
160710253Sxiuyan.wang@Sun.COM 		    mgp->name);
160810253Sxiuyan.wang@Sun.COM 		return (status);
160910253Sxiuyan.wang@Sun.COM 	}
161010253Sxiuyan.wang@Sun.COM 
161110253Sxiuyan.wang@Sun.COM 	*mgp->intr_coal_delay_ptr = htonl(mgp->intr_coal_delay);
161210253Sxiuyan.wang@Sun.COM 	(void) myri10ge_dma_test(mgp, MXGEFW_DMA_TEST);
161310253Sxiuyan.wang@Sun.COM 
161410253Sxiuyan.wang@Sun.COM 	/* reset mcp/driver shared state back to 0 */
161510253Sxiuyan.wang@Sun.COM 
161610253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
161710253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
161810253Sxiuyan.wang@Sun.COM 		bytes = mgp->max_intr_slots *
161910253Sxiuyan.wang@Sun.COM 		    sizeof (*mgp->ss[0].rx_done.entry);
162010253Sxiuyan.wang@Sun.COM 		(void) memset(ss->rx_done.entry, 0, bytes);
162110253Sxiuyan.wang@Sun.COM 		ss->tx.req = 0;
162210253Sxiuyan.wang@Sun.COM 		ss->tx.done = 0;
162310253Sxiuyan.wang@Sun.COM 		ss->tx.pkt_done = 0;
162410253Sxiuyan.wang@Sun.COM 		ss->rx_big.cnt = 0;
162510253Sxiuyan.wang@Sun.COM 		ss->rx_small.cnt = 0;
162610253Sxiuyan.wang@Sun.COM 		ss->rx_done.idx = 0;
162710253Sxiuyan.wang@Sun.COM 		ss->rx_done.cnt = 0;
162810253Sxiuyan.wang@Sun.COM 		ss->rx_token = 0;
162910253Sxiuyan.wang@Sun.COM 		ss->tx.watchdog_done = 0;
163010253Sxiuyan.wang@Sun.COM 		ss->tx.watchdog_req = 0;
163110253Sxiuyan.wang@Sun.COM 		ss->tx.active = 0;
163210253Sxiuyan.wang@Sun.COM 		ss->tx.activate = 0;
163310253Sxiuyan.wang@Sun.COM 	}
163410253Sxiuyan.wang@Sun.COM 	mgp->watchdog_rx_pause = 0;
163510253Sxiuyan.wang@Sun.COM 	if (mgp->ksp_stat != NULL) {
163610253Sxiuyan.wang@Sun.COM 		ethstat = (struct myri10ge_nic_stat *)mgp->ksp_stat->ks_data;
163710253Sxiuyan.wang@Sun.COM 		ethstat->link_changes.value.ul = 0;
163810253Sxiuyan.wang@Sun.COM 	}
163910253Sxiuyan.wang@Sun.COM 	status = myri10ge_m_unicst(mgp, mgp->mac_addr);
164010253Sxiuyan.wang@Sun.COM 	myri10ge_change_promisc(mgp, 0);
164110253Sxiuyan.wang@Sun.COM 	(void) myri10ge_change_pause(mgp, mgp->pause);
164210253Sxiuyan.wang@Sun.COM 	return (status);
164310253Sxiuyan.wang@Sun.COM }
164410253Sxiuyan.wang@Sun.COM 
164510253Sxiuyan.wang@Sun.COM static int
myri10ge_init_toeplitz(struct myri10ge_priv * mgp)164610253Sxiuyan.wang@Sun.COM myri10ge_init_toeplitz(struct myri10ge_priv *mgp)
164710253Sxiuyan.wang@Sun.COM {
164810253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
164910253Sxiuyan.wang@Sun.COM 	int i, b, s, t, j;
165010253Sxiuyan.wang@Sun.COM 	int status;
165110253Sxiuyan.wang@Sun.COM 	uint32_t k[8];
165210253Sxiuyan.wang@Sun.COM 	uint32_t tmp;
165310253Sxiuyan.wang@Sun.COM 	uint8_t *key;
165410253Sxiuyan.wang@Sun.COM 
165510253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RSS_KEY_OFFSET,
165610253Sxiuyan.wang@Sun.COM 	    &cmd);
165710253Sxiuyan.wang@Sun.COM 	if (status != 0) {
165810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed to get rss key\n",
165910253Sxiuyan.wang@Sun.COM 		    mgp->name);
166010253Sxiuyan.wang@Sun.COM 		return (EIO);
166110253Sxiuyan.wang@Sun.COM 	}
166210253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy32(mgp->rss_key,
166310253Sxiuyan.wang@Sun.COM 	    (uint32_t *)(void*)((char *)mgp->sram + cmd.data0),
166410253Sxiuyan.wang@Sun.COM 	    sizeof (mgp->rss_key));
166510253Sxiuyan.wang@Sun.COM 
166610253Sxiuyan.wang@Sun.COM 	mgp->toeplitz_hash_table = kmem_alloc(sizeof (uint32_t) * 12 * 256,
166710253Sxiuyan.wang@Sun.COM 	    KM_SLEEP);
166810253Sxiuyan.wang@Sun.COM 	key = (uint8_t *)mgp->rss_key;
166910253Sxiuyan.wang@Sun.COM 	t = 0;
167010253Sxiuyan.wang@Sun.COM 	for (b = 0; b < 12; b++) {
167110253Sxiuyan.wang@Sun.COM 		for (s = 0; s < 8; s++) {
167210253Sxiuyan.wang@Sun.COM 			/* Bits: b*8+s, ..., b*8+s+31 */
167310253Sxiuyan.wang@Sun.COM 			k[s] = 0;
167410253Sxiuyan.wang@Sun.COM 			for (j = 0; j < 32; j++) {
167510253Sxiuyan.wang@Sun.COM 				int bit = b*8+s+j;
167610253Sxiuyan.wang@Sun.COM 				bit = 0x1 & (key[bit / 8] >> (7 -(bit & 0x7)));
167710253Sxiuyan.wang@Sun.COM 				k[s] |= bit << (31 - j);
167810253Sxiuyan.wang@Sun.COM 			}
167910253Sxiuyan.wang@Sun.COM 		}
168010253Sxiuyan.wang@Sun.COM 
168110253Sxiuyan.wang@Sun.COM 		for (i = 0; i <= 0xff; i++) {
168210253Sxiuyan.wang@Sun.COM 			tmp = 0;
168310253Sxiuyan.wang@Sun.COM 			if (i & (1 << 7)) { tmp ^= k[0]; }
168410253Sxiuyan.wang@Sun.COM 			if (i & (1 << 6)) { tmp ^= k[1]; }
168510253Sxiuyan.wang@Sun.COM 			if (i & (1 << 5)) { tmp ^= k[2]; }
168610253Sxiuyan.wang@Sun.COM 			if (i & (1 << 4)) { tmp ^= k[3]; }
168710253Sxiuyan.wang@Sun.COM 			if (i & (1 << 3)) { tmp ^= k[4]; }
168810253Sxiuyan.wang@Sun.COM 			if (i & (1 << 2)) { tmp ^= k[5]; }
168910253Sxiuyan.wang@Sun.COM 			if (i & (1 << 1)) { tmp ^= k[6]; }
169010253Sxiuyan.wang@Sun.COM 			if (i & (1 << 0)) { tmp ^= k[7]; }
169110253Sxiuyan.wang@Sun.COM 			mgp->toeplitz_hash_table[t++] = tmp;
169210253Sxiuyan.wang@Sun.COM 		}
169310253Sxiuyan.wang@Sun.COM 	}
169410253Sxiuyan.wang@Sun.COM 	return (0);
169510253Sxiuyan.wang@Sun.COM }
169610253Sxiuyan.wang@Sun.COM 
169710253Sxiuyan.wang@Sun.COM static inline struct myri10ge_slice_state *
myri10ge_toeplitz_send_hash(struct myri10ge_priv * mgp,struct ip * ip)169810253Sxiuyan.wang@Sun.COM myri10ge_toeplitz_send_hash(struct myri10ge_priv *mgp, struct ip *ip)
169910253Sxiuyan.wang@Sun.COM {
170010253Sxiuyan.wang@Sun.COM 	struct tcphdr *hdr;
170110253Sxiuyan.wang@Sun.COM 	uint32_t saddr, daddr;
170210253Sxiuyan.wang@Sun.COM 	uint32_t hash, slice;
170310253Sxiuyan.wang@Sun.COM 	uint32_t *table = mgp->toeplitz_hash_table;
170410253Sxiuyan.wang@Sun.COM 	uint16_t src, dst;
170510253Sxiuyan.wang@Sun.COM 
170610253Sxiuyan.wang@Sun.COM 	/*
170710253Sxiuyan.wang@Sun.COM 	 * Note hashing order is reversed from how it is done
170810253Sxiuyan.wang@Sun.COM 	 * in the NIC, so as to generate the same hash value
170910253Sxiuyan.wang@Sun.COM 	 * for the connection to try to keep connections CPU local
171010253Sxiuyan.wang@Sun.COM 	 */
171110253Sxiuyan.wang@Sun.COM 
171210253Sxiuyan.wang@Sun.COM 	/* hash on IPv4 src/dst address */
171310253Sxiuyan.wang@Sun.COM 	saddr = ntohl(ip->ip_src.s_addr);
171410253Sxiuyan.wang@Sun.COM 	daddr = ntohl(ip->ip_dst.s_addr);
171510253Sxiuyan.wang@Sun.COM 	hash = table[(256 * 0) + ((daddr >> 24) & 0xff)];
171610253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 1) + ((daddr >> 16) & 0xff)];
171710253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 2) + ((daddr >> 8) & 0xff)];
171810253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 3) + ((daddr) & 0xff)];
171910253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 4) + ((saddr >> 24) & 0xff)];
172010253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 5) + ((saddr >> 16) & 0xff)];
172110253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 6) + ((saddr >> 8) & 0xff)];
172210253Sxiuyan.wang@Sun.COM 	hash ^= table[(256 * 7) + ((saddr) & 0xff)];
172310253Sxiuyan.wang@Sun.COM 	/* hash on TCP port, if required */
172410253Sxiuyan.wang@Sun.COM 	if ((myri10ge_rss_hash & MXGEFW_RSS_HASH_TYPE_TCP_IPV4) &&
172510253Sxiuyan.wang@Sun.COM 	    ip->ip_p == IPPROTO_TCP) {
172610253Sxiuyan.wang@Sun.COM 		hdr = (struct tcphdr *)(void *)
172710253Sxiuyan.wang@Sun.COM 		    (((uint8_t *)ip) +  (ip->ip_hl << 2));
172810253Sxiuyan.wang@Sun.COM 		src = ntohs(hdr->th_sport);
172910253Sxiuyan.wang@Sun.COM 		dst = ntohs(hdr->th_dport);
173010253Sxiuyan.wang@Sun.COM 
173110253Sxiuyan.wang@Sun.COM 		hash ^= table[(256 * 8) + ((dst >> 8) & 0xff)];
173210253Sxiuyan.wang@Sun.COM 		hash ^= table[(256 * 9) + ((dst) & 0xff)];
173310253Sxiuyan.wang@Sun.COM 		hash ^= table[(256 * 10) + ((src >> 8) & 0xff)];
173410253Sxiuyan.wang@Sun.COM 		hash ^= table[(256 * 11) + ((src) & 0xff)];
173510253Sxiuyan.wang@Sun.COM 	}
173610253Sxiuyan.wang@Sun.COM 	slice = (mgp->num_slices - 1) & hash;
173710253Sxiuyan.wang@Sun.COM 	return (&mgp->ss[slice]);
173810253Sxiuyan.wang@Sun.COM 
173910253Sxiuyan.wang@Sun.COM }
174010253Sxiuyan.wang@Sun.COM 
174110253Sxiuyan.wang@Sun.COM static inline struct myri10ge_slice_state *
myri10ge_simple_send_hash(struct myri10ge_priv * mgp,struct ip * ip)174210253Sxiuyan.wang@Sun.COM myri10ge_simple_send_hash(struct myri10ge_priv *mgp, struct ip *ip)
174310253Sxiuyan.wang@Sun.COM {
174410253Sxiuyan.wang@Sun.COM 	struct tcphdr *hdr;
174510253Sxiuyan.wang@Sun.COM 	uint32_t slice, hash_val;
174610253Sxiuyan.wang@Sun.COM 
174710253Sxiuyan.wang@Sun.COM 
174810253Sxiuyan.wang@Sun.COM 	if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP) {
174910253Sxiuyan.wang@Sun.COM 		return (&mgp->ss[0]);
175010253Sxiuyan.wang@Sun.COM 	}
175110253Sxiuyan.wang@Sun.COM 	hdr = (struct tcphdr *)(void *)(((uint8_t *)ip) +  (ip->ip_hl << 2));
175210253Sxiuyan.wang@Sun.COM 
175310253Sxiuyan.wang@Sun.COM 	/*
175410253Sxiuyan.wang@Sun.COM 	 * Use the second byte of the *destination* address for
175510253Sxiuyan.wang@Sun.COM 	 * MXGEFW_RSS_HASH_TYPE_SRC_PORT, so as to match NIC's hashing
175610253Sxiuyan.wang@Sun.COM 	 */
175710253Sxiuyan.wang@Sun.COM 	hash_val = ntohs(hdr->th_dport) & 0xff;
175810253Sxiuyan.wang@Sun.COM 	if (myri10ge_rss_hash == MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT)
175910253Sxiuyan.wang@Sun.COM 		hash_val += ntohs(hdr->th_sport) & 0xff;
176010253Sxiuyan.wang@Sun.COM 
176110253Sxiuyan.wang@Sun.COM 	slice = (mgp->num_slices - 1) & hash_val;
176210253Sxiuyan.wang@Sun.COM 	return (&mgp->ss[slice]);
176310253Sxiuyan.wang@Sun.COM }
176410253Sxiuyan.wang@Sun.COM 
176510253Sxiuyan.wang@Sun.COM static inline struct myri10ge_slice_state *
myri10ge_send_hash(struct myri10ge_priv * mgp,mblk_t * mp)176610253Sxiuyan.wang@Sun.COM myri10ge_send_hash(struct myri10ge_priv *mgp, mblk_t *mp)
176710253Sxiuyan.wang@Sun.COM {
176810253Sxiuyan.wang@Sun.COM 	unsigned int slice = 0;
176910253Sxiuyan.wang@Sun.COM 	struct ether_header *eh;
177010253Sxiuyan.wang@Sun.COM 	struct ether_vlan_header *vh;
177110253Sxiuyan.wang@Sun.COM 	struct ip *ip;
177210253Sxiuyan.wang@Sun.COM 	int ehl, ihl;
177310253Sxiuyan.wang@Sun.COM 
177410253Sxiuyan.wang@Sun.COM 	if (mgp->num_slices == 1)
177510253Sxiuyan.wang@Sun.COM 		return (&mgp->ss[0]);
177610253Sxiuyan.wang@Sun.COM 
177710253Sxiuyan.wang@Sun.COM 	if (myri10ge_tx_hash == 0) {
177810253Sxiuyan.wang@Sun.COM 		slice = CPU->cpu_id & (mgp->num_slices - 1);
177910253Sxiuyan.wang@Sun.COM 		return (&mgp->ss[slice]);
178010253Sxiuyan.wang@Sun.COM 	}
178110253Sxiuyan.wang@Sun.COM 
178210253Sxiuyan.wang@Sun.COM 	/*
178310253Sxiuyan.wang@Sun.COM 	 *  ensure it is a TCP or UDP over IPv4 packet, and that the
178410253Sxiuyan.wang@Sun.COM 	 *  headers are in the 1st mblk.  Otherwise, punt
178510253Sxiuyan.wang@Sun.COM 	 */
178610253Sxiuyan.wang@Sun.COM 	ehl = sizeof (*eh);
178710253Sxiuyan.wang@Sun.COM 	ihl = sizeof (*ip);
178810253Sxiuyan.wang@Sun.COM 	if ((MBLKL(mp)) <  (ehl + ihl + 8))
178910253Sxiuyan.wang@Sun.COM 		return (&mgp->ss[0]);
179010253Sxiuyan.wang@Sun.COM 	eh = (struct ether_header *)(void *)mp->b_rptr;
179110253Sxiuyan.wang@Sun.COM 	ip = (struct ip *)(void *)(eh + 1);
179210253Sxiuyan.wang@Sun.COM 	if (eh->ether_type != BE_16(ETHERTYPE_IP)) {
179310253Sxiuyan.wang@Sun.COM 		if (eh->ether_type != BE_16(ETHERTYPE_VLAN))
179410253Sxiuyan.wang@Sun.COM 			return (&mgp->ss[0]);
179510253Sxiuyan.wang@Sun.COM 		vh = (struct ether_vlan_header *)(void *)mp->b_rptr;
179610253Sxiuyan.wang@Sun.COM 		if (vh->ether_type != BE_16(ETHERTYPE_IP))
179710253Sxiuyan.wang@Sun.COM 			return (&mgp->ss[0]);
179810253Sxiuyan.wang@Sun.COM 		ehl += 4;
179910253Sxiuyan.wang@Sun.COM 		ip = (struct ip *)(void *)(vh + 1);
180010253Sxiuyan.wang@Sun.COM 	}
180110253Sxiuyan.wang@Sun.COM 	ihl = ip->ip_hl << 2;
180210253Sxiuyan.wang@Sun.COM 	if (MBLKL(mp) <  (ehl + ihl + 8))
180310253Sxiuyan.wang@Sun.COM 		return (&mgp->ss[0]);
180410253Sxiuyan.wang@Sun.COM 	switch (myri10ge_rss_hash) {
180510253Sxiuyan.wang@Sun.COM 	case MXGEFW_RSS_HASH_TYPE_IPV4:
180610253Sxiuyan.wang@Sun.COM 		/* fallthru */
180710253Sxiuyan.wang@Sun.COM 	case MXGEFW_RSS_HASH_TYPE_TCP_IPV4:
180810253Sxiuyan.wang@Sun.COM 		/* fallthru */
180910253Sxiuyan.wang@Sun.COM 	case (MXGEFW_RSS_HASH_TYPE_IPV4|MXGEFW_RSS_HASH_TYPE_TCP_IPV4):
181010253Sxiuyan.wang@Sun.COM 		return (myri10ge_toeplitz_send_hash(mgp, ip));
181110253Sxiuyan.wang@Sun.COM 	case MXGEFW_RSS_HASH_TYPE_SRC_PORT:
181210253Sxiuyan.wang@Sun.COM 		/* fallthru */
181310253Sxiuyan.wang@Sun.COM 	case MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT:
181410253Sxiuyan.wang@Sun.COM 		return (myri10ge_simple_send_hash(mgp, ip));
181510253Sxiuyan.wang@Sun.COM 	default:
181610253Sxiuyan.wang@Sun.COM 		break;
181710253Sxiuyan.wang@Sun.COM 	}
181810253Sxiuyan.wang@Sun.COM 	return (&mgp->ss[0]);
181910253Sxiuyan.wang@Sun.COM }
182010253Sxiuyan.wang@Sun.COM 
182110253Sxiuyan.wang@Sun.COM static int
myri10ge_setup_slice(struct myri10ge_slice_state * ss)182210253Sxiuyan.wang@Sun.COM myri10ge_setup_slice(struct myri10ge_slice_state *ss)
182310253Sxiuyan.wang@Sun.COM {
182410253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
182510253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
182610253Sxiuyan.wang@Sun.COM 	int tx_ring_size, rx_ring_size;
182710253Sxiuyan.wang@Sun.COM 	int tx_ring_entries, rx_ring_entries;
182810253Sxiuyan.wang@Sun.COM 	int slice, status;
182910253Sxiuyan.wang@Sun.COM 	int allocated, idx;
183010253Sxiuyan.wang@Sun.COM 	size_t bytes;
183110253Sxiuyan.wang@Sun.COM 
183210253Sxiuyan.wang@Sun.COM 	slice = ss - mgp->ss;
183310253Sxiuyan.wang@Sun.COM 	cmd.data0 = slice;
183410253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
183510253Sxiuyan.wang@Sun.COM 	tx_ring_size = cmd.data0;
183610253Sxiuyan.wang@Sun.COM 	cmd.data0 = slice;
183710253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
183810253Sxiuyan.wang@Sun.COM 	if (status != 0)
183910253Sxiuyan.wang@Sun.COM 		return (status);
184010253Sxiuyan.wang@Sun.COM 	rx_ring_size = cmd.data0;
184110253Sxiuyan.wang@Sun.COM 
184210253Sxiuyan.wang@Sun.COM 	tx_ring_entries = tx_ring_size / sizeof (struct mcp_kreq_ether_send);
184310253Sxiuyan.wang@Sun.COM 	rx_ring_entries = rx_ring_size / sizeof (struct mcp_dma_addr);
184410253Sxiuyan.wang@Sun.COM 	ss->tx.mask = tx_ring_entries - 1;
184510253Sxiuyan.wang@Sun.COM 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
184610253Sxiuyan.wang@Sun.COM 
184710253Sxiuyan.wang@Sun.COM 	/* get the lanai pointers to the send and receive rings */
184810253Sxiuyan.wang@Sun.COM 
184910253Sxiuyan.wang@Sun.COM 	cmd.data0 = slice;
185010253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
185110253Sxiuyan.wang@Sun.COM 	ss->tx.lanai = (mcp_kreq_ether_send_t *)(void *)(mgp->sram + cmd.data0);
185210253Sxiuyan.wang@Sun.COM 	if (mgp->num_slices > 1) {
185310253Sxiuyan.wang@Sun.COM 		ss->tx.go = (char *)mgp->sram + MXGEFW_ETH_SEND_GO + 64 * slice;
185410253Sxiuyan.wang@Sun.COM 		ss->tx.stop = (char *)mgp->sram + MXGEFW_ETH_SEND_STOP +
185510253Sxiuyan.wang@Sun.COM 		    64 * slice;
185610253Sxiuyan.wang@Sun.COM 	} else {
185710253Sxiuyan.wang@Sun.COM 		ss->tx.go = NULL;
185810253Sxiuyan.wang@Sun.COM 		ss->tx.stop = NULL;
185910253Sxiuyan.wang@Sun.COM 	}
186010253Sxiuyan.wang@Sun.COM 
186110253Sxiuyan.wang@Sun.COM 	cmd.data0 = slice;
186210253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
186310253Sxiuyan.wang@Sun.COM 	ss->rx_small.lanai = (mcp_kreq_ether_recv_t *)
186410253Sxiuyan.wang@Sun.COM 	    (void *)(mgp->sram + cmd.data0);
186510253Sxiuyan.wang@Sun.COM 
186610253Sxiuyan.wang@Sun.COM 	cmd.data0 = slice;
186710253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
186810253Sxiuyan.wang@Sun.COM 	ss->rx_big.lanai = (mcp_kreq_ether_recv_t *)(void *)
186910253Sxiuyan.wang@Sun.COM 	    (mgp->sram + cmd.data0);
187010253Sxiuyan.wang@Sun.COM 
187110253Sxiuyan.wang@Sun.COM 	if (status != 0) {
187210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
187310253Sxiuyan.wang@Sun.COM 		    "%s: failed to get ring sizes or locations\n", mgp->name);
187410253Sxiuyan.wang@Sun.COM 		return (status);
187510253Sxiuyan.wang@Sun.COM 	}
187610253Sxiuyan.wang@Sun.COM 
187710253Sxiuyan.wang@Sun.COM 	status = ENOMEM;
187810253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
187910253Sxiuyan.wang@Sun.COM 	ss->rx_small.shadow = kmem_zalloc(bytes, KM_SLEEP);
188010253Sxiuyan.wang@Sun.COM 	if (ss->rx_small.shadow == NULL)
188110253Sxiuyan.wang@Sun.COM 		goto abort;
188210253Sxiuyan.wang@Sun.COM 	(void) memset(ss->rx_small.shadow, 0, bytes);
188310253Sxiuyan.wang@Sun.COM 
188410253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
188510253Sxiuyan.wang@Sun.COM 	ss->rx_big.shadow = kmem_zalloc(bytes, KM_SLEEP);
188610253Sxiuyan.wang@Sun.COM 	if (ss->rx_big.shadow == NULL)
188710253Sxiuyan.wang@Sun.COM 		goto abort_with_rx_small_shadow;
188810253Sxiuyan.wang@Sun.COM 	(void) memset(ss->rx_big.shadow, 0, bytes);
188910253Sxiuyan.wang@Sun.COM 
189010253Sxiuyan.wang@Sun.COM 	/* allocate the host info rings */
189110253Sxiuyan.wang@Sun.COM 
189210253Sxiuyan.wang@Sun.COM 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
189310253Sxiuyan.wang@Sun.COM 	ss->tx.info = kmem_zalloc(bytes, KM_SLEEP);
189410253Sxiuyan.wang@Sun.COM 	if (ss->tx.info == NULL)
189510253Sxiuyan.wang@Sun.COM 		goto abort_with_rx_big_shadow;
189610253Sxiuyan.wang@Sun.COM 	(void) memset(ss->tx.info, 0, bytes);
189710253Sxiuyan.wang@Sun.COM 
189810253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
189910253Sxiuyan.wang@Sun.COM 	ss->rx_small.info = kmem_zalloc(bytes, KM_SLEEP);
190010253Sxiuyan.wang@Sun.COM 	if (ss->rx_small.info == NULL)
190110253Sxiuyan.wang@Sun.COM 		goto abort_with_tx_info;
190210253Sxiuyan.wang@Sun.COM 	(void) memset(ss->rx_small.info, 0, bytes);
190310253Sxiuyan.wang@Sun.COM 
190410253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
190510253Sxiuyan.wang@Sun.COM 	ss->rx_big.info = kmem_zalloc(bytes, KM_SLEEP);
190610253Sxiuyan.wang@Sun.COM 	if (ss->rx_big.info == NULL)
190710253Sxiuyan.wang@Sun.COM 		goto abort_with_rx_small_info;
190810253Sxiuyan.wang@Sun.COM 	(void) memset(ss->rx_big.info, 0, bytes);
190910253Sxiuyan.wang@Sun.COM 
191010253Sxiuyan.wang@Sun.COM 	ss->tx.stall = ss->tx.sched = 0;
191110253Sxiuyan.wang@Sun.COM 	ss->tx.stall_early = ss->tx.stall_late = 0;
191210253Sxiuyan.wang@Sun.COM 
191310253Sxiuyan.wang@Sun.COM 	ss->jbufs_for_smalls = 1 + (1 + ss->rx_small.mask) /
191410253Sxiuyan.wang@Sun.COM 	    (myri10ge_mtu / (myri10ge_small_bytes + MXGEFW_PAD));
191510253Sxiuyan.wang@Sun.COM 
191610253Sxiuyan.wang@Sun.COM 	allocated = myri10ge_add_jbufs(ss,
191710253Sxiuyan.wang@Sun.COM 	    myri10ge_bigbufs_initial + ss->jbufs_for_smalls, 1);
191810253Sxiuyan.wang@Sun.COM 	if (allocated < ss->jbufs_for_smalls + myri10ge_bigbufs_initial) {
191910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
192010253Sxiuyan.wang@Sun.COM 		    "%s: Could not allocate enough receive buffers (%d/%d)\n",
192110253Sxiuyan.wang@Sun.COM 		    mgp->name, allocated,
192210253Sxiuyan.wang@Sun.COM 		    myri10ge_bigbufs_initial + ss->jbufs_for_smalls);
192310253Sxiuyan.wang@Sun.COM 		goto abort_with_jumbos;
192410253Sxiuyan.wang@Sun.COM 	}
192510253Sxiuyan.wang@Sun.COM 
192610253Sxiuyan.wang@Sun.COM 	myri10ge_carve_up_jbufs_into_small_ring(ss);
192710253Sxiuyan.wang@Sun.COM 	ss->j_rx_cnt = 0;
192810253Sxiuyan.wang@Sun.COM 
192910253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->jpool.mtx);
193010253Sxiuyan.wang@Sun.COM 	if (allocated < rx_ring_entries)
193110253Sxiuyan.wang@Sun.COM 		ss->jpool.low_water = allocated / 4;
193210253Sxiuyan.wang@Sun.COM 	else
193310253Sxiuyan.wang@Sun.COM 		ss->jpool.low_water = rx_ring_entries / 2;
193410253Sxiuyan.wang@Sun.COM 
193510253Sxiuyan.wang@Sun.COM 	/*
193610253Sxiuyan.wang@Sun.COM 	 * invalidate the big receive ring in case we do not
193710253Sxiuyan.wang@Sun.COM 	 * allocate sufficient jumbos to fill it
193810253Sxiuyan.wang@Sun.COM 	 */
193910253Sxiuyan.wang@Sun.COM 	(void) memset(ss->rx_big.shadow, 1,
194010253Sxiuyan.wang@Sun.COM 	    (ss->rx_big.mask + 1) * sizeof (ss->rx_big.shadow[0]));
194110253Sxiuyan.wang@Sun.COM 	for (idx = 7; idx <= ss->rx_big.mask; idx += 8) {
194210253Sxiuyan.wang@Sun.COM 		myri10ge_submit_8rx(&ss->rx_big.lanai[idx - 7],
194310253Sxiuyan.wang@Sun.COM 		    &ss->rx_big.shadow[idx - 7]);
194410253Sxiuyan.wang@Sun.COM 		mb();
194510253Sxiuyan.wang@Sun.COM 	}
194610253Sxiuyan.wang@Sun.COM 
194710253Sxiuyan.wang@Sun.COM 
194810253Sxiuyan.wang@Sun.COM 	myri10ge_restock_jumbos(ss);
194910253Sxiuyan.wang@Sun.COM 
195010253Sxiuyan.wang@Sun.COM 	for (idx = 7; idx <= ss->rx_small.mask; idx += 8) {
195110253Sxiuyan.wang@Sun.COM 		myri10ge_submit_8rx(&ss->rx_small.lanai[idx - 7],
195210253Sxiuyan.wang@Sun.COM 		    &ss->rx_small.shadow[idx - 7]);
195310253Sxiuyan.wang@Sun.COM 		mb();
195410253Sxiuyan.wang@Sun.COM 	}
195510253Sxiuyan.wang@Sun.COM 	ss->rx_small.cnt = ss->rx_small.mask + 1;
195610253Sxiuyan.wang@Sun.COM 
195710253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->jpool.mtx);
195810253Sxiuyan.wang@Sun.COM 
195910253Sxiuyan.wang@Sun.COM 	status = myri10ge_prepare_tx_ring(ss);
196010253Sxiuyan.wang@Sun.COM 
196110253Sxiuyan.wang@Sun.COM 	if (status != 0)
196210253Sxiuyan.wang@Sun.COM 		goto abort_with_small_jbufs;
196310253Sxiuyan.wang@Sun.COM 
196410253Sxiuyan.wang@Sun.COM 	cmd.data0 = ntohl(ss->fw_stats_dma.low);
196510253Sxiuyan.wang@Sun.COM 	cmd.data1 = ntohl(ss->fw_stats_dma.high);
196610253Sxiuyan.wang@Sun.COM 	cmd.data2 = sizeof (mcp_irq_data_t);
196710253Sxiuyan.wang@Sun.COM 	cmd.data2 |= (slice << 16);
196810253Sxiuyan.wang@Sun.COM 	bzero(ss->fw_stats, sizeof (*ss->fw_stats));
196910253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
197010253Sxiuyan.wang@Sun.COM 	if (status == ENOSYS) {
197110253Sxiuyan.wang@Sun.COM 		cmd.data0 = ntohl(ss->fw_stats_dma.low) +
197210253Sxiuyan.wang@Sun.COM 		    offsetof(mcp_irq_data_t, send_done_count);
197310253Sxiuyan.wang@Sun.COM 		cmd.data1 = ntohl(ss->fw_stats_dma.high);
197410253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp,
197510253Sxiuyan.wang@Sun.COM 		    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, &cmd);
197610253Sxiuyan.wang@Sun.COM 	}
197710253Sxiuyan.wang@Sun.COM 	if (status) {
197810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Couldn't set stats DMA\n", mgp->name);
197910253Sxiuyan.wang@Sun.COM 		goto abort_with_tx;
198010253Sxiuyan.wang@Sun.COM 	}
198110253Sxiuyan.wang@Sun.COM 
198210253Sxiuyan.wang@Sun.COM 	return (0);
198310253Sxiuyan.wang@Sun.COM 
198410253Sxiuyan.wang@Sun.COM abort_with_tx:
198510253Sxiuyan.wang@Sun.COM 	myri10ge_unprepare_tx_ring(ss);
198610253Sxiuyan.wang@Sun.COM 
198710253Sxiuyan.wang@Sun.COM abort_with_small_jbufs:
198810253Sxiuyan.wang@Sun.COM 	myri10ge_release_small_jbufs(ss);
198910253Sxiuyan.wang@Sun.COM 
199010253Sxiuyan.wang@Sun.COM abort_with_jumbos:
199110253Sxiuyan.wang@Sun.COM 	if (allocated != 0) {
199210253Sxiuyan.wang@Sun.COM 		mutex_enter(&ss->jpool.mtx);
199310253Sxiuyan.wang@Sun.COM 		ss->jpool.low_water = 0;
199410253Sxiuyan.wang@Sun.COM 		mutex_exit(&ss->jpool.mtx);
199510253Sxiuyan.wang@Sun.COM 		myri10ge_unstock_jumbos(ss);
199610253Sxiuyan.wang@Sun.COM 		myri10ge_remove_jbufs(ss);
199710253Sxiuyan.wang@Sun.COM 	}
199810253Sxiuyan.wang@Sun.COM 
199910253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
200010253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_big.info, bytes);
200110253Sxiuyan.wang@Sun.COM 
200210253Sxiuyan.wang@Sun.COM abort_with_rx_small_info:
200310253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
200410253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_small.info, bytes);
200510253Sxiuyan.wang@Sun.COM 
200610253Sxiuyan.wang@Sun.COM abort_with_tx_info:
200710253Sxiuyan.wang@Sun.COM 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
200810253Sxiuyan.wang@Sun.COM 	kmem_free(ss->tx.info, bytes);
200910253Sxiuyan.wang@Sun.COM 
201010253Sxiuyan.wang@Sun.COM abort_with_rx_big_shadow:
201110253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
201210253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_big.shadow, bytes);
201310253Sxiuyan.wang@Sun.COM 
201410253Sxiuyan.wang@Sun.COM abort_with_rx_small_shadow:
201510253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
201610253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_small.shadow, bytes);
201710253Sxiuyan.wang@Sun.COM abort:
201810253Sxiuyan.wang@Sun.COM 	return (status);
201910253Sxiuyan.wang@Sun.COM 
202010253Sxiuyan.wang@Sun.COM }
202110253Sxiuyan.wang@Sun.COM 
202210253Sxiuyan.wang@Sun.COM static void
myri10ge_teardown_slice(struct myri10ge_slice_state * ss)202310253Sxiuyan.wang@Sun.COM myri10ge_teardown_slice(struct myri10ge_slice_state *ss)
202410253Sxiuyan.wang@Sun.COM {
202510253Sxiuyan.wang@Sun.COM 	int tx_ring_entries, rx_ring_entries;
202610253Sxiuyan.wang@Sun.COM 	size_t bytes;
202710253Sxiuyan.wang@Sun.COM 
202810253Sxiuyan.wang@Sun.COM 	/* ignore slices that have not been fully setup */
202910253Sxiuyan.wang@Sun.COM 	if (ss->tx.cp == NULL)
203010253Sxiuyan.wang@Sun.COM 		return;
203110253Sxiuyan.wang@Sun.COM 	/* Free the TX copy buffers */
203210253Sxiuyan.wang@Sun.COM 	myri10ge_unprepare_tx_ring(ss);
203310253Sxiuyan.wang@Sun.COM 
203410253Sxiuyan.wang@Sun.COM 	/* stop passing returned buffers to firmware */
203510253Sxiuyan.wang@Sun.COM 
203610253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->jpool.mtx);
203710253Sxiuyan.wang@Sun.COM 	ss->jpool.low_water = 0;
203810253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->jpool.mtx);
203910253Sxiuyan.wang@Sun.COM 	myri10ge_release_small_jbufs(ss);
204010253Sxiuyan.wang@Sun.COM 
204110253Sxiuyan.wang@Sun.COM 	/* Release the free jumbo frame pool */
204210253Sxiuyan.wang@Sun.COM 	myri10ge_unstock_jumbos(ss);
204310253Sxiuyan.wang@Sun.COM 	myri10ge_remove_jbufs(ss);
204410253Sxiuyan.wang@Sun.COM 
204510253Sxiuyan.wang@Sun.COM 	rx_ring_entries = ss->rx_big.mask + 1;
204610253Sxiuyan.wang@Sun.COM 	tx_ring_entries = ss->tx.mask + 1;
204710253Sxiuyan.wang@Sun.COM 
204810253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
204910253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_big.info, bytes);
205010253Sxiuyan.wang@Sun.COM 
205110253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
205210253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_small.info, bytes);
205310253Sxiuyan.wang@Sun.COM 
205410253Sxiuyan.wang@Sun.COM 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
205510253Sxiuyan.wang@Sun.COM 	kmem_free(ss->tx.info, bytes);
205610253Sxiuyan.wang@Sun.COM 
205710253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
205810253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_big.shadow, bytes);
205910253Sxiuyan.wang@Sun.COM 
206010253Sxiuyan.wang@Sun.COM 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
206110253Sxiuyan.wang@Sun.COM 	kmem_free(ss->rx_small.shadow, bytes);
206210253Sxiuyan.wang@Sun.COM 
206310253Sxiuyan.wang@Sun.COM }
206410253Sxiuyan.wang@Sun.COM static int
myri10ge_start_locked(struct myri10ge_priv * mgp)206510253Sxiuyan.wang@Sun.COM myri10ge_start_locked(struct myri10ge_priv *mgp)
206610253Sxiuyan.wang@Sun.COM {
206710253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
206810253Sxiuyan.wang@Sun.COM 	int status, big_pow2, i;
206910253Sxiuyan.wang@Sun.COM 	volatile uint8_t *itable;
207010253Sxiuyan.wang@Sun.COM 
207110253Sxiuyan.wang@Sun.COM 	status = DDI_SUCCESS;
207210253Sxiuyan.wang@Sun.COM 	/* Allocate DMA resources and receive buffers */
207310253Sxiuyan.wang@Sun.COM 
207410253Sxiuyan.wang@Sun.COM 	status = myri10ge_reset(mgp);
207510253Sxiuyan.wang@Sun.COM 	if (status != 0) {
207610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed reset\n", mgp->name);
207710253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
207810253Sxiuyan.wang@Sun.COM 	}
207910253Sxiuyan.wang@Sun.COM 
208010253Sxiuyan.wang@Sun.COM 	if (mgp->num_slices > 1) {
208110253Sxiuyan.wang@Sun.COM 		cmd.data0 = mgp->num_slices;
208210253Sxiuyan.wang@Sun.COM 		cmd.data1 = 1; /* use MSI-X */
208310253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
208410253Sxiuyan.wang@Sun.COM 		    &cmd);
208510253Sxiuyan.wang@Sun.COM 		if (status != 0) {
208610253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
208710253Sxiuyan.wang@Sun.COM 			    "%s: failed to set number of slices\n",
208810253Sxiuyan.wang@Sun.COM 			    mgp->name);
208910253Sxiuyan.wang@Sun.COM 			goto abort_with_nothing;
209010253Sxiuyan.wang@Sun.COM 		}
209110253Sxiuyan.wang@Sun.COM 		/* setup the indirection table */
209210253Sxiuyan.wang@Sun.COM 		cmd.data0 = mgp->num_slices;
209310253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
209410253Sxiuyan.wang@Sun.COM 		    &cmd);
209510253Sxiuyan.wang@Sun.COM 
209610253Sxiuyan.wang@Sun.COM 		status |= myri10ge_send_cmd(mgp,
209710253Sxiuyan.wang@Sun.COM 		    MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
209810253Sxiuyan.wang@Sun.COM 		if (status != 0) {
209910253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
210010253Sxiuyan.wang@Sun.COM 			    "%s: failed to setup rss tables\n", mgp->name);
210110253Sxiuyan.wang@Sun.COM 		}
210210253Sxiuyan.wang@Sun.COM 
210310253Sxiuyan.wang@Sun.COM 		/* just enable an identity mapping */
210410253Sxiuyan.wang@Sun.COM 		itable = mgp->sram + cmd.data0;
210510253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++)
210610253Sxiuyan.wang@Sun.COM 			itable[i] = (uint8_t)i;
210710253Sxiuyan.wang@Sun.COM 
210810253Sxiuyan.wang@Sun.COM 		if (myri10ge_rss_hash & MYRI10GE_TOEPLITZ_HASH) {
210910253Sxiuyan.wang@Sun.COM 			status = myri10ge_init_toeplitz(mgp);
211010253Sxiuyan.wang@Sun.COM 			if (status != 0) {
211110253Sxiuyan.wang@Sun.COM 				cmn_err(CE_WARN, "%s: failed to setup "
211210253Sxiuyan.wang@Sun.COM 				    "toeplitz tx hash table", mgp->name);
211310253Sxiuyan.wang@Sun.COM 				goto abort_with_nothing;
211410253Sxiuyan.wang@Sun.COM 			}
211510253Sxiuyan.wang@Sun.COM 		}
211610253Sxiuyan.wang@Sun.COM 		cmd.data0 = 1;
211710253Sxiuyan.wang@Sun.COM 		cmd.data1 = myri10ge_rss_hash;
211810253Sxiuyan.wang@Sun.COM 		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_ENABLE,
211910253Sxiuyan.wang@Sun.COM 		    &cmd);
212010253Sxiuyan.wang@Sun.COM 		if (status != 0) {
212110253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
212210253Sxiuyan.wang@Sun.COM 			    "%s: failed to enable slices\n", mgp->name);
212310253Sxiuyan.wang@Sun.COM 			goto abort_with_toeplitz;
212410253Sxiuyan.wang@Sun.COM 		}
212510253Sxiuyan.wang@Sun.COM 	}
212610253Sxiuyan.wang@Sun.COM 
212710253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
212810253Sxiuyan.wang@Sun.COM 		status = myri10ge_setup_slice(&mgp->ss[i]);
212910253Sxiuyan.wang@Sun.COM 		if (status != 0)
213010253Sxiuyan.wang@Sun.COM 			goto abort_with_slices;
213110253Sxiuyan.wang@Sun.COM 	}
213210253Sxiuyan.wang@Sun.COM 
213310253Sxiuyan.wang@Sun.COM 	/*
213410253Sxiuyan.wang@Sun.COM 	 * Tell the MCP how many buffers he has, and to
213510253Sxiuyan.wang@Sun.COM 	 *  bring the ethernet interface up
213610253Sxiuyan.wang@Sun.COM 	 *
213710253Sxiuyan.wang@Sun.COM 	 * Firmware needs the big buff size as a power of 2.  Lie and
213810253Sxiuyan.wang@Sun.COM 	 * tell him the buffer is larger, because we only use 1
213910253Sxiuyan.wang@Sun.COM 	 * buffer/pkt, and the mtu will prevent overruns
214010253Sxiuyan.wang@Sun.COM 	 */
214110253Sxiuyan.wang@Sun.COM 	big_pow2 = myri10ge_mtu + MXGEFW_PAD;
214210253Sxiuyan.wang@Sun.COM 	while ((big_pow2 & (big_pow2 - 1)) != 0)
214310253Sxiuyan.wang@Sun.COM 		big_pow2++;
214410253Sxiuyan.wang@Sun.COM 
214510253Sxiuyan.wang@Sun.COM 	/* now give firmware buffers sizes, and MTU */
214610253Sxiuyan.wang@Sun.COM 	cmd.data0 = myri10ge_mtu;
214710253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_MTU, &cmd);
214810253Sxiuyan.wang@Sun.COM 	cmd.data0 = myri10ge_small_bytes;
214910253Sxiuyan.wang@Sun.COM 	status |=
215010253Sxiuyan.wang@Sun.COM 	    myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
215110253Sxiuyan.wang@Sun.COM 	cmd.data0 = big_pow2;
215210253Sxiuyan.wang@Sun.COM 	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
215310253Sxiuyan.wang@Sun.COM 	if (status) {
215410253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Couldn't set buffer sizes\n", mgp->name);
215510253Sxiuyan.wang@Sun.COM 		goto abort_with_slices;
215610253Sxiuyan.wang@Sun.COM 	}
215710253Sxiuyan.wang@Sun.COM 
215810253Sxiuyan.wang@Sun.COM 
215910253Sxiuyan.wang@Sun.COM 	cmd.data0 = 1;
216010253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_TSO_MODE, &cmd);
216110253Sxiuyan.wang@Sun.COM 	if (status) {
216210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: unable to setup TSO (%d)\n",
216310253Sxiuyan.wang@Sun.COM 		    mgp->name, status);
216410253Sxiuyan.wang@Sun.COM 	} else {
216510253Sxiuyan.wang@Sun.COM 		mgp->features |= MYRI10GE_TSO;
216610253Sxiuyan.wang@Sun.COM 	}
216710253Sxiuyan.wang@Sun.COM 
216810253Sxiuyan.wang@Sun.COM 	mgp->link_state = -1;
216910253Sxiuyan.wang@Sun.COM 	mgp->rdma_tags_available = 15;
217010253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd);
217110253Sxiuyan.wang@Sun.COM 	if (status) {
217210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: unable to start ethernet\n", mgp->name);
217310253Sxiuyan.wang@Sun.COM 		goto abort_with_slices;
217410253Sxiuyan.wang@Sun.COM 	}
217510253Sxiuyan.wang@Sun.COM 	mgp->running = MYRI10GE_ETH_RUNNING;
217610253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
217710253Sxiuyan.wang@Sun.COM 
217810253Sxiuyan.wang@Sun.COM abort_with_slices:
217910253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++)
218010253Sxiuyan.wang@Sun.COM 		myri10ge_teardown_slice(&mgp->ss[i]);
218110253Sxiuyan.wang@Sun.COM 
218210253Sxiuyan.wang@Sun.COM 	mgp->running = MYRI10GE_ETH_STOPPED;
218310253Sxiuyan.wang@Sun.COM 
218410253Sxiuyan.wang@Sun.COM abort_with_toeplitz:
218510253Sxiuyan.wang@Sun.COM 	if (mgp->toeplitz_hash_table != NULL) {
218610253Sxiuyan.wang@Sun.COM 		kmem_free(mgp->toeplitz_hash_table,
218710253Sxiuyan.wang@Sun.COM 		    sizeof (uint32_t) * 12 * 256);
218810253Sxiuyan.wang@Sun.COM 		mgp->toeplitz_hash_table = NULL;
218910253Sxiuyan.wang@Sun.COM 	}
219010253Sxiuyan.wang@Sun.COM 
219110253Sxiuyan.wang@Sun.COM abort_with_nothing:
219210253Sxiuyan.wang@Sun.COM 	return (DDI_FAILURE);
219310253Sxiuyan.wang@Sun.COM }
219410253Sxiuyan.wang@Sun.COM 
219510253Sxiuyan.wang@Sun.COM static void
myri10ge_stop_locked(struct myri10ge_priv * mgp)219610253Sxiuyan.wang@Sun.COM myri10ge_stop_locked(struct myri10ge_priv *mgp)
219710253Sxiuyan.wang@Sun.COM {
219810253Sxiuyan.wang@Sun.COM 	int status, old_down_cnt;
219910253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
220010253Sxiuyan.wang@Sun.COM 	int wait_time = 10;
220110253Sxiuyan.wang@Sun.COM 	int i, polling;
220210253Sxiuyan.wang@Sun.COM 
220310253Sxiuyan.wang@Sun.COM 	old_down_cnt = mgp->down_cnt;
220410253Sxiuyan.wang@Sun.COM 	mb();
220510253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
220610253Sxiuyan.wang@Sun.COM 	if (status) {
220710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Couldn't bring down link\n", mgp->name);
220810253Sxiuyan.wang@Sun.COM 	}
220910253Sxiuyan.wang@Sun.COM 
221010253Sxiuyan.wang@Sun.COM 	while (old_down_cnt == *((volatile int *)&mgp->down_cnt)) {
221110253Sxiuyan.wang@Sun.COM 		delay(1 * drv_usectohz(1000000));
221210253Sxiuyan.wang@Sun.COM 		wait_time--;
221310253Sxiuyan.wang@Sun.COM 		if (wait_time == 0)
221410253Sxiuyan.wang@Sun.COM 			break;
221510253Sxiuyan.wang@Sun.COM 	}
221610253Sxiuyan.wang@Sun.COM again:
221710253Sxiuyan.wang@Sun.COM 	if (old_down_cnt == *((volatile int *)&mgp->down_cnt)) {
221810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: didn't get down irq\n", mgp->name);
221910253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
222010253Sxiuyan.wang@Sun.COM 			/*
222110253Sxiuyan.wang@Sun.COM 			 * take and release the rx lock to ensure
222210253Sxiuyan.wang@Sun.COM 			 * that no interrupt thread is blocked
222310253Sxiuyan.wang@Sun.COM 			 * elsewhere in the stack, preventing
222410253Sxiuyan.wang@Sun.COM 			 * completion
222510253Sxiuyan.wang@Sun.COM 			 */
222610253Sxiuyan.wang@Sun.COM 
222710253Sxiuyan.wang@Sun.COM 			mutex_enter(&mgp->ss[i].rx_lock);
222810253Sxiuyan.wang@Sun.COM 			printf("%s: slice %d rx irq idle\n",
222910253Sxiuyan.wang@Sun.COM 			    mgp->name, i);
223010253Sxiuyan.wang@Sun.COM 			mutex_exit(&mgp->ss[i].rx_lock);
223110253Sxiuyan.wang@Sun.COM 
223210253Sxiuyan.wang@Sun.COM 			/* verify that the poll handler is inactive */
223310253Sxiuyan.wang@Sun.COM 			mutex_enter(&mgp->ss->poll_lock);
223410253Sxiuyan.wang@Sun.COM 			polling = mgp->ss->rx_polling;
223510253Sxiuyan.wang@Sun.COM 			mutex_exit(&mgp->ss->poll_lock);
223610253Sxiuyan.wang@Sun.COM 			if (polling) {
223710253Sxiuyan.wang@Sun.COM 				printf("%s: slice %d is polling\n",
223810253Sxiuyan.wang@Sun.COM 				    mgp->name, i);
223910253Sxiuyan.wang@Sun.COM 				delay(1 * drv_usectohz(1000000));
224010253Sxiuyan.wang@Sun.COM 				goto again;
224110253Sxiuyan.wang@Sun.COM 			}
224210253Sxiuyan.wang@Sun.COM 		}
224310253Sxiuyan.wang@Sun.COM 		delay(1 * drv_usectohz(1000000));
224410253Sxiuyan.wang@Sun.COM 		if (old_down_cnt == *((volatile int *)&mgp->down_cnt)) {
224510253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN, "%s: Never got down irq\n", mgp->name);
224610253Sxiuyan.wang@Sun.COM 		}
224710253Sxiuyan.wang@Sun.COM 	}
224810253Sxiuyan.wang@Sun.COM 
224910253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++)
225010253Sxiuyan.wang@Sun.COM 		myri10ge_teardown_slice(&mgp->ss[i]);
225110253Sxiuyan.wang@Sun.COM 
225210253Sxiuyan.wang@Sun.COM 	if (mgp->toeplitz_hash_table != NULL) {
225310253Sxiuyan.wang@Sun.COM 		kmem_free(mgp->toeplitz_hash_table,
225410253Sxiuyan.wang@Sun.COM 		    sizeof (uint32_t) * 12 * 256);
225510253Sxiuyan.wang@Sun.COM 		mgp->toeplitz_hash_table = NULL;
225610253Sxiuyan.wang@Sun.COM 	}
225710253Sxiuyan.wang@Sun.COM 	mgp->running = MYRI10GE_ETH_STOPPED;
225810253Sxiuyan.wang@Sun.COM }
225910253Sxiuyan.wang@Sun.COM 
226010253Sxiuyan.wang@Sun.COM static int
myri10ge_m_start(void * arg)226110253Sxiuyan.wang@Sun.COM myri10ge_m_start(void *arg)
226210253Sxiuyan.wang@Sun.COM {
226310253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
226410253Sxiuyan.wang@Sun.COM 	int status;
226510253Sxiuyan.wang@Sun.COM 
226610253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
226710253Sxiuyan.wang@Sun.COM 
226810253Sxiuyan.wang@Sun.COM 	if (mgp->running != MYRI10GE_ETH_STOPPED) {
226910253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
227010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
227110253Sxiuyan.wang@Sun.COM 	}
227210253Sxiuyan.wang@Sun.COM 	status = myri10ge_start_locked(mgp);
227310253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
227410253Sxiuyan.wang@Sun.COM 
227510253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS)
227610253Sxiuyan.wang@Sun.COM 		return (status);
227710253Sxiuyan.wang@Sun.COM 
227810253Sxiuyan.wang@Sun.COM 	/* start the watchdog timer */
227910253Sxiuyan.wang@Sun.COM 	mgp->timer_id = timeout(myri10ge_watchdog, mgp,
228010253Sxiuyan.wang@Sun.COM 	    mgp->timer_ticks);
228110253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
228210253Sxiuyan.wang@Sun.COM 
228310253Sxiuyan.wang@Sun.COM }
228410253Sxiuyan.wang@Sun.COM 
228510253Sxiuyan.wang@Sun.COM static void
myri10ge_m_stop(void * arg)228610253Sxiuyan.wang@Sun.COM myri10ge_m_stop(void *arg)
228710253Sxiuyan.wang@Sun.COM {
228810253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
228910253Sxiuyan.wang@Sun.COM 
229010253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
229110253Sxiuyan.wang@Sun.COM 	/* if the device not running give up */
229210253Sxiuyan.wang@Sun.COM 	if (mgp->running != MYRI10GE_ETH_RUNNING) {
229310253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
229410253Sxiuyan.wang@Sun.COM 		return;
229510253Sxiuyan.wang@Sun.COM 	}
229610253Sxiuyan.wang@Sun.COM 
229710253Sxiuyan.wang@Sun.COM 	mgp->running = MYRI10GE_ETH_STOPPING;
229810253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
229910253Sxiuyan.wang@Sun.COM 	(void) untimeout(mgp->timer_id);
230010253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
230110253Sxiuyan.wang@Sun.COM 	myri10ge_stop_locked(mgp);
230210253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
230310253Sxiuyan.wang@Sun.COM 
230410253Sxiuyan.wang@Sun.COM }
230510253Sxiuyan.wang@Sun.COM 
230610253Sxiuyan.wang@Sun.COM static inline void
myri10ge_rx_csum(mblk_t * mp,struct myri10ge_rx_ring_stats * s,uint32_t csum)230710253Sxiuyan.wang@Sun.COM myri10ge_rx_csum(mblk_t *mp, struct myri10ge_rx_ring_stats *s, uint32_t csum)
230810253Sxiuyan.wang@Sun.COM {
230910253Sxiuyan.wang@Sun.COM 	struct ether_header *eh;
231010253Sxiuyan.wang@Sun.COM 	struct ip *ip;
231110253Sxiuyan.wang@Sun.COM 	struct ip6_hdr *ip6;
231210253Sxiuyan.wang@Sun.COM 	uint32_t start, stuff, end, partial, hdrlen;
231310253Sxiuyan.wang@Sun.COM 
231410253Sxiuyan.wang@Sun.COM 
231510253Sxiuyan.wang@Sun.COM 	csum = ntohs((uint16_t)csum);
231610253Sxiuyan.wang@Sun.COM 	eh = (struct ether_header *)(void *)mp->b_rptr;
231710253Sxiuyan.wang@Sun.COM 	hdrlen = sizeof (*eh);
231810253Sxiuyan.wang@Sun.COM 	if (eh->ether_dhost.ether_addr_octet[0] & 1) {
231910253Sxiuyan.wang@Sun.COM 		if (0 == (bcmp(eh->ether_dhost.ether_addr_octet,
232010253Sxiuyan.wang@Sun.COM 		    myri10ge_broadcastaddr, sizeof (eh->ether_dhost))))
232110253Sxiuyan.wang@Sun.COM 			s->brdcstrcv++;
232210253Sxiuyan.wang@Sun.COM 		else
232310253Sxiuyan.wang@Sun.COM 			s->multircv++;
232410253Sxiuyan.wang@Sun.COM 	}
232510253Sxiuyan.wang@Sun.COM 
232610253Sxiuyan.wang@Sun.COM 	if (eh->ether_type == BE_16(ETHERTYPE_VLAN)) {
232710253Sxiuyan.wang@Sun.COM 		/*
232810253Sxiuyan.wang@Sun.COM 		 * fix checksum by subtracting 4 bytes after what the
232910253Sxiuyan.wang@Sun.COM 		 * firmware thought was the end of the ether hdr
233010253Sxiuyan.wang@Sun.COM 		 */
233110253Sxiuyan.wang@Sun.COM 		partial = *(uint32_t *)
233210253Sxiuyan.wang@Sun.COM 		    (void *)(mp->b_rptr + ETHERNET_HEADER_SIZE);
233310253Sxiuyan.wang@Sun.COM 		csum += ~partial;
233410253Sxiuyan.wang@Sun.COM 		csum +=  (csum < ~partial);
233510253Sxiuyan.wang@Sun.COM 		csum = (csum >> 16) + (csum & 0xFFFF);
233610253Sxiuyan.wang@Sun.COM 		csum = (csum >> 16) + (csum & 0xFFFF);
233710253Sxiuyan.wang@Sun.COM 		hdrlen += VLAN_TAGSZ;
233810253Sxiuyan.wang@Sun.COM 	}
233910253Sxiuyan.wang@Sun.COM 
234010253Sxiuyan.wang@Sun.COM 	if (eh->ether_type ==  BE_16(ETHERTYPE_IP)) {
234110253Sxiuyan.wang@Sun.COM 		ip = (struct ip *)(void *)(mp->b_rptr + hdrlen);
234210253Sxiuyan.wang@Sun.COM 		start = ip->ip_hl << 2;
234310253Sxiuyan.wang@Sun.COM 
234410253Sxiuyan.wang@Sun.COM 		if (ip->ip_p == IPPROTO_TCP)
234510253Sxiuyan.wang@Sun.COM 			stuff = start + offsetof(struct tcphdr, th_sum);
234610253Sxiuyan.wang@Sun.COM 		else if (ip->ip_p == IPPROTO_UDP)
234710253Sxiuyan.wang@Sun.COM 			stuff = start + offsetof(struct udphdr, uh_sum);
234810253Sxiuyan.wang@Sun.COM 		else
234910253Sxiuyan.wang@Sun.COM 			return;
235010253Sxiuyan.wang@Sun.COM 		end = ntohs(ip->ip_len);
235110253Sxiuyan.wang@Sun.COM 	} else if (eh->ether_type ==  BE_16(ETHERTYPE_IPV6)) {
235210253Sxiuyan.wang@Sun.COM 		ip6 = (struct ip6_hdr *)(void *)(mp->b_rptr + hdrlen);
235310253Sxiuyan.wang@Sun.COM 		start = sizeof (*ip6);
235410253Sxiuyan.wang@Sun.COM 		if (ip6->ip6_nxt == IPPROTO_TCP) {
235510253Sxiuyan.wang@Sun.COM 			stuff = start + offsetof(struct tcphdr, th_sum);
235610253Sxiuyan.wang@Sun.COM 		} else if (ip6->ip6_nxt == IPPROTO_UDP)
235710253Sxiuyan.wang@Sun.COM 			stuff = start + offsetof(struct udphdr, uh_sum);
235810253Sxiuyan.wang@Sun.COM 		else
235910253Sxiuyan.wang@Sun.COM 			return;
236010253Sxiuyan.wang@Sun.COM 		end = start + ntohs(ip6->ip6_plen);
236110253Sxiuyan.wang@Sun.COM 		/*
236210253Sxiuyan.wang@Sun.COM 		 * IPv6 headers do not contain a checksum, and hence
236310253Sxiuyan.wang@Sun.COM 		 * do not checksum to zero, so they don't "fall out"
236410253Sxiuyan.wang@Sun.COM 		 * of the partial checksum calculation like IPv4
236510253Sxiuyan.wang@Sun.COM 		 * headers do.  We need to fix the partial checksum by
236610253Sxiuyan.wang@Sun.COM 		 * subtracting the checksum of the IPv6 header.
236710253Sxiuyan.wang@Sun.COM 		 */
236810253Sxiuyan.wang@Sun.COM 
236910253Sxiuyan.wang@Sun.COM 		partial = myri10ge_csum_generic((uint16_t *)ip6, sizeof (*ip6));
237010253Sxiuyan.wang@Sun.COM 		csum += ~partial;
237110253Sxiuyan.wang@Sun.COM 		csum +=  (csum < ~partial);
237210253Sxiuyan.wang@Sun.COM 		csum = (csum >> 16) + (csum & 0xFFFF);
237310253Sxiuyan.wang@Sun.COM 		csum = (csum >> 16) + (csum & 0xFFFF);
237410253Sxiuyan.wang@Sun.COM 	} else {
237510253Sxiuyan.wang@Sun.COM 		return;
237610253Sxiuyan.wang@Sun.COM 	}
237710253Sxiuyan.wang@Sun.COM 
237810253Sxiuyan.wang@Sun.COM 	if (MBLKL(mp) > hdrlen + end) {
237910253Sxiuyan.wang@Sun.COM 		/* padded frame, so hw csum may be invalid */
238010253Sxiuyan.wang@Sun.COM 		return;
238110253Sxiuyan.wang@Sun.COM 	}
238210253Sxiuyan.wang@Sun.COM 
2383*11878SVenu.Iyer@Sun.COM 	mac_hcksum_set(mp, start, stuff, end, csum, HCK_PARTIALCKSUM);
238410253Sxiuyan.wang@Sun.COM }
238510253Sxiuyan.wang@Sun.COM 
238610253Sxiuyan.wang@Sun.COM static mblk_t *
myri10ge_rx_done_small(struct myri10ge_slice_state * ss,uint32_t len,uint32_t csum)238710253Sxiuyan.wang@Sun.COM myri10ge_rx_done_small(struct myri10ge_slice_state *ss, uint32_t len,
238810253Sxiuyan.wang@Sun.COM     uint32_t csum)
238910253Sxiuyan.wang@Sun.COM {
239010253Sxiuyan.wang@Sun.COM 	mblk_t *mp;
239110253Sxiuyan.wang@Sun.COM 	myri10ge_rx_ring_t *rx;
239210253Sxiuyan.wang@Sun.COM 	int idx;
239310253Sxiuyan.wang@Sun.COM 
239410253Sxiuyan.wang@Sun.COM 	rx = &ss->rx_small;
239510253Sxiuyan.wang@Sun.COM 	idx = rx->cnt & rx->mask;
239610253Sxiuyan.wang@Sun.COM 	ss->rx_small.cnt++;
239710253Sxiuyan.wang@Sun.COM 
239810253Sxiuyan.wang@Sun.COM 	/* allocate a new buffer to pass up the stack */
239910253Sxiuyan.wang@Sun.COM 	mp = allocb(len + MXGEFW_PAD, 0);
240010253Sxiuyan.wang@Sun.COM 	if (mp == NULL) {
240110253Sxiuyan.wang@Sun.COM 		MYRI10GE_ATOMIC_SLICE_STAT_INC(rx_small_nobuf);
240210253Sxiuyan.wang@Sun.COM 		goto abort;
240310253Sxiuyan.wang@Sun.COM 	}
240410253Sxiuyan.wang@Sun.COM 	bcopy(ss->rx_small.info[idx].ptr,
240510253Sxiuyan.wang@Sun.COM 	    (caddr_t)mp->b_wptr, len + MXGEFW_PAD);
240610253Sxiuyan.wang@Sun.COM 	mp->b_wptr += len + MXGEFW_PAD;
240710253Sxiuyan.wang@Sun.COM 	mp->b_rptr += MXGEFW_PAD;
240810253Sxiuyan.wang@Sun.COM 
240910253Sxiuyan.wang@Sun.COM 	ss->rx_stats.ibytes += len;
241010253Sxiuyan.wang@Sun.COM 	ss->rx_stats.ipackets += 1;
241110253Sxiuyan.wang@Sun.COM 	myri10ge_rx_csum(mp, &ss->rx_stats, csum);
241210253Sxiuyan.wang@Sun.COM 
241310253Sxiuyan.wang@Sun.COM abort:
241410253Sxiuyan.wang@Sun.COM 	if ((idx & 7) == 7) {
241510253Sxiuyan.wang@Sun.COM 		myri10ge_submit_8rx(&rx->lanai[idx - 7],
241610253Sxiuyan.wang@Sun.COM 		    &rx->shadow[idx - 7]);
241710253Sxiuyan.wang@Sun.COM 	}
241810253Sxiuyan.wang@Sun.COM 
241910253Sxiuyan.wang@Sun.COM 	return (mp);
242010253Sxiuyan.wang@Sun.COM }
242110253Sxiuyan.wang@Sun.COM 
242210253Sxiuyan.wang@Sun.COM 
242310253Sxiuyan.wang@Sun.COM static mblk_t *
myri10ge_rx_done_big(struct myri10ge_slice_state * ss,uint32_t len,uint32_t csum)242410253Sxiuyan.wang@Sun.COM myri10ge_rx_done_big(struct myri10ge_slice_state *ss, uint32_t len,
242510253Sxiuyan.wang@Sun.COM     uint32_t csum)
242610253Sxiuyan.wang@Sun.COM {
242710253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_stuff *jpool;
242810253Sxiuyan.wang@Sun.COM 	struct myri10ge_jpool_entry *j;
242910253Sxiuyan.wang@Sun.COM 	mblk_t *mp;
243010253Sxiuyan.wang@Sun.COM 	int idx, num_owned_by_mcp;
243110253Sxiuyan.wang@Sun.COM 
243210253Sxiuyan.wang@Sun.COM 	jpool = &ss->jpool;
243310253Sxiuyan.wang@Sun.COM 	idx = ss->j_rx_cnt & ss->rx_big.mask;
243410253Sxiuyan.wang@Sun.COM 	j = ss->rx_big.info[idx].j;
243510253Sxiuyan.wang@Sun.COM 
243610253Sxiuyan.wang@Sun.COM 	if (j == NULL) {
243710253Sxiuyan.wang@Sun.COM 		printf("%s: null j at idx=%d, rx_big.cnt = %d, j_rx_cnt=%d\n",
243810253Sxiuyan.wang@Sun.COM 		    ss->mgp->name, idx, ss->rx_big.cnt, ss->j_rx_cnt);
243910253Sxiuyan.wang@Sun.COM 		return (NULL);
244010253Sxiuyan.wang@Sun.COM 	}
244110253Sxiuyan.wang@Sun.COM 
244210253Sxiuyan.wang@Sun.COM 
244310253Sxiuyan.wang@Sun.COM 	ss->rx_big.info[idx].j = NULL;
244410253Sxiuyan.wang@Sun.COM 	ss->j_rx_cnt++;
244510253Sxiuyan.wang@Sun.COM 
244610253Sxiuyan.wang@Sun.COM 
244710253Sxiuyan.wang@Sun.COM 	/*
244810253Sxiuyan.wang@Sun.COM 	 * Check to see if we are low on rx buffers.
244910253Sxiuyan.wang@Sun.COM 	 * Note that we must leave at least 8 free so there are
245010253Sxiuyan.wang@Sun.COM 	 * enough to free in a single 64-byte write.
245110253Sxiuyan.wang@Sun.COM 	 */
245210253Sxiuyan.wang@Sun.COM 	num_owned_by_mcp = ss->rx_big.cnt - ss->j_rx_cnt;
245310253Sxiuyan.wang@Sun.COM 	if (num_owned_by_mcp < jpool->low_water) {
245410253Sxiuyan.wang@Sun.COM 		mutex_enter(&jpool->mtx);
245510253Sxiuyan.wang@Sun.COM 		myri10ge_restock_jumbos(ss);
245610253Sxiuyan.wang@Sun.COM 		mutex_exit(&jpool->mtx);
245710253Sxiuyan.wang@Sun.COM 		num_owned_by_mcp = ss->rx_big.cnt - ss->j_rx_cnt;
245810253Sxiuyan.wang@Sun.COM 		/* if we are still low, then we have to copy */
245910253Sxiuyan.wang@Sun.COM 		if (num_owned_by_mcp < 16) {
246010253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(rx_copy);
246110253Sxiuyan.wang@Sun.COM 			/* allocate a new buffer to pass up the stack */
246210253Sxiuyan.wang@Sun.COM 			mp = allocb(len + MXGEFW_PAD, 0);
246310253Sxiuyan.wang@Sun.COM 			if (mp == NULL) {
246410253Sxiuyan.wang@Sun.COM 				goto abort;
246510253Sxiuyan.wang@Sun.COM 			}
246610253Sxiuyan.wang@Sun.COM 			bcopy(j->buf,
246710253Sxiuyan.wang@Sun.COM 			    (caddr_t)mp->b_wptr, len + MXGEFW_PAD);
246810253Sxiuyan.wang@Sun.COM 			myri10ge_jfree_rtn(j);
246910253Sxiuyan.wang@Sun.COM 			/* push buffer back to NIC */
247010253Sxiuyan.wang@Sun.COM 			mutex_enter(&jpool->mtx);
247110253Sxiuyan.wang@Sun.COM 			myri10ge_restock_jumbos(ss);
247210253Sxiuyan.wang@Sun.COM 			mutex_exit(&jpool->mtx);
247310253Sxiuyan.wang@Sun.COM 			goto set_len;
247410253Sxiuyan.wang@Sun.COM 		}
247510253Sxiuyan.wang@Sun.COM 	}
247610253Sxiuyan.wang@Sun.COM 
247710253Sxiuyan.wang@Sun.COM 	/* loan our buffer to the stack */
247810253Sxiuyan.wang@Sun.COM 	mp = desballoc((unsigned char *)j->buf, myri10ge_mtu, 0, &j->free_func);
247910253Sxiuyan.wang@Sun.COM 	if (mp == NULL) {
248010253Sxiuyan.wang@Sun.COM 		goto abort;
248110253Sxiuyan.wang@Sun.COM 	}
248210253Sxiuyan.wang@Sun.COM 
248310253Sxiuyan.wang@Sun.COM set_len:
248410253Sxiuyan.wang@Sun.COM 	mp->b_rptr += MXGEFW_PAD;
248510253Sxiuyan.wang@Sun.COM 	mp->b_wptr = ((unsigned char *) mp->b_rptr + len);
248610253Sxiuyan.wang@Sun.COM 
248710253Sxiuyan.wang@Sun.COM 	ss->rx_stats.ibytes += len;
248810253Sxiuyan.wang@Sun.COM 	ss->rx_stats.ipackets += 1;
248910253Sxiuyan.wang@Sun.COM 	myri10ge_rx_csum(mp, &ss->rx_stats, csum);
249010253Sxiuyan.wang@Sun.COM 
249110253Sxiuyan.wang@Sun.COM 	return (mp);
249210253Sxiuyan.wang@Sun.COM 
249310253Sxiuyan.wang@Sun.COM abort:
249410253Sxiuyan.wang@Sun.COM 	myri10ge_jfree_rtn(j);
249510253Sxiuyan.wang@Sun.COM 	MYRI10GE_ATOMIC_SLICE_STAT_INC(rx_big_nobuf);
249610253Sxiuyan.wang@Sun.COM 	return (NULL);
249710253Sxiuyan.wang@Sun.COM }
249810253Sxiuyan.wang@Sun.COM 
249910253Sxiuyan.wang@Sun.COM /*
250010253Sxiuyan.wang@Sun.COM  * Free all transmit buffers up until the specified index
250110253Sxiuyan.wang@Sun.COM  */
250210253Sxiuyan.wang@Sun.COM static inline void
myri10ge_tx_done(struct myri10ge_slice_state * ss,uint32_t mcp_index)250310253Sxiuyan.wang@Sun.COM myri10ge_tx_done(struct myri10ge_slice_state *ss, uint32_t mcp_index)
250410253Sxiuyan.wang@Sun.COM {
250510253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx;
250610253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle_head handles;
250710253Sxiuyan.wang@Sun.COM 	int idx;
250810253Sxiuyan.wang@Sun.COM 	int limit = 0;
250910253Sxiuyan.wang@Sun.COM 
251010253Sxiuyan.wang@Sun.COM 	tx = &ss->tx;
251110253Sxiuyan.wang@Sun.COM 	handles.head = NULL;
251210253Sxiuyan.wang@Sun.COM 	handles.tail = NULL;
251310253Sxiuyan.wang@Sun.COM 	while (tx->pkt_done != (int)mcp_index) {
251410253Sxiuyan.wang@Sun.COM 		idx = tx->done & tx->mask;
251510253Sxiuyan.wang@Sun.COM 
251610253Sxiuyan.wang@Sun.COM 		/*
251710253Sxiuyan.wang@Sun.COM 		 * mblk & DMA handle attached only to first slot
251810253Sxiuyan.wang@Sun.COM 		 * per buffer in the packet
251910253Sxiuyan.wang@Sun.COM 		 */
252010253Sxiuyan.wang@Sun.COM 
252110253Sxiuyan.wang@Sun.COM 		if (tx->info[idx].m) {
252210253Sxiuyan.wang@Sun.COM 			(void) ddi_dma_unbind_handle(tx->info[idx].handle->h);
252310253Sxiuyan.wang@Sun.COM 			tx->info[idx].handle->next = handles.head;
252410253Sxiuyan.wang@Sun.COM 			handles.head = tx->info[idx].handle;
252510253Sxiuyan.wang@Sun.COM 			if (handles.tail == NULL)
252610253Sxiuyan.wang@Sun.COM 				handles.tail = tx->info[idx].handle;
252710253Sxiuyan.wang@Sun.COM 			freeb(tx->info[idx].m);
252810253Sxiuyan.wang@Sun.COM 			tx->info[idx].m = 0;
252910253Sxiuyan.wang@Sun.COM 			tx->info[idx].handle = 0;
253010253Sxiuyan.wang@Sun.COM 		}
253110253Sxiuyan.wang@Sun.COM 		if (tx->info[idx].ostat.opackets != 0) {
253210253Sxiuyan.wang@Sun.COM 			tx->stats.multixmt += tx->info[idx].ostat.multixmt;
253310253Sxiuyan.wang@Sun.COM 			tx->stats.brdcstxmt += tx->info[idx].ostat.brdcstxmt;
253410253Sxiuyan.wang@Sun.COM 			tx->stats.obytes += tx->info[idx].ostat.obytes;
253510253Sxiuyan.wang@Sun.COM 			tx->stats.opackets += tx->info[idx].ostat.opackets;
253610253Sxiuyan.wang@Sun.COM 			tx->info[idx].stat.un.all = 0;
253710253Sxiuyan.wang@Sun.COM 			tx->pkt_done++;
253810253Sxiuyan.wang@Sun.COM 		}
253910253Sxiuyan.wang@Sun.COM 
254010253Sxiuyan.wang@Sun.COM 		tx->done++;
254110253Sxiuyan.wang@Sun.COM 		/*
254210253Sxiuyan.wang@Sun.COM 		 * if we stalled the queue, wake it.  But Wait until
254310253Sxiuyan.wang@Sun.COM 		 * we have at least 1/2 our slots free.
254410253Sxiuyan.wang@Sun.COM 		 */
254510253Sxiuyan.wang@Sun.COM 		if ((tx->req - tx->done) < (tx->mask >> 1) &&
254610253Sxiuyan.wang@Sun.COM 		    tx->stall != tx->sched) {
254710253Sxiuyan.wang@Sun.COM 			mutex_enter(&ss->tx.lock);
254810253Sxiuyan.wang@Sun.COM 			tx->sched = tx->stall;
254910253Sxiuyan.wang@Sun.COM 			mutex_exit(&ss->tx.lock);
255010253Sxiuyan.wang@Sun.COM 			mac_tx_ring_update(ss->mgp->mh, tx->rh);
255110253Sxiuyan.wang@Sun.COM 		}
255210253Sxiuyan.wang@Sun.COM 
255310253Sxiuyan.wang@Sun.COM 		/* limit potential for livelock */
255410253Sxiuyan.wang@Sun.COM 		if (unlikely(++limit >  2 * tx->mask))
255510253Sxiuyan.wang@Sun.COM 			break;
255610253Sxiuyan.wang@Sun.COM 	}
255710253Sxiuyan.wang@Sun.COM 	if (tx->req == tx->done && tx->stop != NULL) {
255810253Sxiuyan.wang@Sun.COM 		/*
255910253Sxiuyan.wang@Sun.COM 		 * Nic has sent all pending requests, allow him
256010253Sxiuyan.wang@Sun.COM 		 * to stop polling this queue
256110253Sxiuyan.wang@Sun.COM 		 */
256210253Sxiuyan.wang@Sun.COM 		mutex_enter(&tx->lock);
256310253Sxiuyan.wang@Sun.COM 		if (tx->req == tx->done && tx->active) {
256410253Sxiuyan.wang@Sun.COM 			*(int *)(void *)tx->stop = 1;
256510253Sxiuyan.wang@Sun.COM 			tx->active = 0;
256610253Sxiuyan.wang@Sun.COM 			mb();
256710253Sxiuyan.wang@Sun.COM 		}
256810253Sxiuyan.wang@Sun.COM 		mutex_exit(&tx->lock);
256910253Sxiuyan.wang@Sun.COM 	}
257010253Sxiuyan.wang@Sun.COM 	if (handles.head != NULL)
257110253Sxiuyan.wang@Sun.COM 		myri10ge_free_tx_handles(tx, &handles);
257210253Sxiuyan.wang@Sun.COM }
257310253Sxiuyan.wang@Sun.COM 
257410253Sxiuyan.wang@Sun.COM static void
myri10ge_mbl_init(struct myri10ge_mblk_list * mbl)257510253Sxiuyan.wang@Sun.COM myri10ge_mbl_init(struct myri10ge_mblk_list *mbl)
257610253Sxiuyan.wang@Sun.COM {
257710253Sxiuyan.wang@Sun.COM 	mbl->head = NULL;
257810253Sxiuyan.wang@Sun.COM 	mbl->tail = &mbl->head;
257910253Sxiuyan.wang@Sun.COM 	mbl->cnt = 0;
258010253Sxiuyan.wang@Sun.COM }
258110253Sxiuyan.wang@Sun.COM 
258210253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
258310253Sxiuyan.wang@Sun.COM void
myri10ge_mbl_append(struct myri10ge_slice_state * ss,struct myri10ge_mblk_list * mbl,mblk_t * mp)258410253Sxiuyan.wang@Sun.COM myri10ge_mbl_append(struct myri10ge_slice_state *ss,
258510253Sxiuyan.wang@Sun.COM     struct myri10ge_mblk_list *mbl, mblk_t *mp)
258610253Sxiuyan.wang@Sun.COM {
258710253Sxiuyan.wang@Sun.COM 	*(mbl->tail) = mp;
258810253Sxiuyan.wang@Sun.COM 	mbl->tail = &mp->b_next;
258910253Sxiuyan.wang@Sun.COM 	mp->b_next = NULL;
259010253Sxiuyan.wang@Sun.COM 	mbl->cnt++;
259110253Sxiuyan.wang@Sun.COM }
259210253Sxiuyan.wang@Sun.COM 
259310253Sxiuyan.wang@Sun.COM 
259410253Sxiuyan.wang@Sun.COM static inline void
myri10ge_clean_rx_done(struct myri10ge_slice_state * ss,struct myri10ge_mblk_list * mbl,int limit,boolean_t * stop)259510253Sxiuyan.wang@Sun.COM myri10ge_clean_rx_done(struct myri10ge_slice_state *ss,
259610253Sxiuyan.wang@Sun.COM     struct myri10ge_mblk_list *mbl, int limit, boolean_t *stop)
259710253Sxiuyan.wang@Sun.COM {
259810253Sxiuyan.wang@Sun.COM 	myri10ge_rx_done_t *rx_done = &ss->rx_done;
259910253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
260010253Sxiuyan.wang@Sun.COM 	mblk_t *mp;
260110253Sxiuyan.wang@Sun.COM 	struct lro_entry *lro;
260210253Sxiuyan.wang@Sun.COM 	uint16_t length;
260310253Sxiuyan.wang@Sun.COM 	uint16_t checksum;
260410253Sxiuyan.wang@Sun.COM 
260510253Sxiuyan.wang@Sun.COM 
260610253Sxiuyan.wang@Sun.COM 	while (rx_done->entry[rx_done->idx].length != 0) {
260710253Sxiuyan.wang@Sun.COM 		if (unlikely (*stop)) {
260810253Sxiuyan.wang@Sun.COM 			break;
260910253Sxiuyan.wang@Sun.COM 		}
261010253Sxiuyan.wang@Sun.COM 		length = ntohs(rx_done->entry[rx_done->idx].length);
261110253Sxiuyan.wang@Sun.COM 		length &= (~MXGEFW_RSS_HASH_MASK);
261210253Sxiuyan.wang@Sun.COM 
261310253Sxiuyan.wang@Sun.COM 		/* limit potential for livelock */
261410253Sxiuyan.wang@Sun.COM 		limit -= length;
261510253Sxiuyan.wang@Sun.COM 		if (unlikely(limit < 0))
261610253Sxiuyan.wang@Sun.COM 			break;
261710253Sxiuyan.wang@Sun.COM 
261810253Sxiuyan.wang@Sun.COM 		rx_done->entry[rx_done->idx].length = 0;
261910253Sxiuyan.wang@Sun.COM 		checksum = ntohs(rx_done->entry[rx_done->idx].checksum);
262010253Sxiuyan.wang@Sun.COM 		if (length <= myri10ge_small_bytes)
262110253Sxiuyan.wang@Sun.COM 			mp = myri10ge_rx_done_small(ss, length, checksum);
262210253Sxiuyan.wang@Sun.COM 		else
262310253Sxiuyan.wang@Sun.COM 			mp = myri10ge_rx_done_big(ss, length, checksum);
262410253Sxiuyan.wang@Sun.COM 		if (mp != NULL) {
262510253Sxiuyan.wang@Sun.COM 			if (!myri10ge_lro ||
262610253Sxiuyan.wang@Sun.COM 			    0 != myri10ge_lro_rx(ss, mp, checksum, mbl))
262710253Sxiuyan.wang@Sun.COM 				myri10ge_mbl_append(ss, mbl, mp);
262810253Sxiuyan.wang@Sun.COM 		}
262910253Sxiuyan.wang@Sun.COM 		rx_done->cnt++;
263010253Sxiuyan.wang@Sun.COM 		rx_done->idx = rx_done->cnt & (mgp->max_intr_slots - 1);
263110253Sxiuyan.wang@Sun.COM 	}
263210253Sxiuyan.wang@Sun.COM 	while (ss->lro_active != NULL) {
263310253Sxiuyan.wang@Sun.COM 		lro = ss->lro_active;
263410253Sxiuyan.wang@Sun.COM 		ss->lro_active = lro->next;
263510253Sxiuyan.wang@Sun.COM 		myri10ge_lro_flush(ss, lro, mbl);
263610253Sxiuyan.wang@Sun.COM 	}
263710253Sxiuyan.wang@Sun.COM }
263810253Sxiuyan.wang@Sun.COM 
263910253Sxiuyan.wang@Sun.COM static void
myri10ge_intr_rx(struct myri10ge_slice_state * ss)264010253Sxiuyan.wang@Sun.COM myri10ge_intr_rx(struct myri10ge_slice_state *ss)
264110253Sxiuyan.wang@Sun.COM {
264210253Sxiuyan.wang@Sun.COM 	uint64_t gen;
264310253Sxiuyan.wang@Sun.COM 	struct myri10ge_mblk_list mbl;
264410253Sxiuyan.wang@Sun.COM 
264510253Sxiuyan.wang@Sun.COM 	myri10ge_mbl_init(&mbl);
264610253Sxiuyan.wang@Sun.COM 	if (mutex_tryenter(&ss->rx_lock) == 0)
264710253Sxiuyan.wang@Sun.COM 		return;
264810253Sxiuyan.wang@Sun.COM 	gen = ss->rx_gen_num;
264910253Sxiuyan.wang@Sun.COM 	myri10ge_clean_rx_done(ss, &mbl, MYRI10GE_POLL_NULL,
265010253Sxiuyan.wang@Sun.COM 	    &ss->rx_polling);
265110253Sxiuyan.wang@Sun.COM 	if (mbl.head != NULL)
265210253Sxiuyan.wang@Sun.COM 		mac_rx_ring(ss->mgp->mh, ss->rx_rh, mbl.head, gen);
265310253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->rx_lock);
265410253Sxiuyan.wang@Sun.COM 
265510253Sxiuyan.wang@Sun.COM }
265610253Sxiuyan.wang@Sun.COM 
265710253Sxiuyan.wang@Sun.COM static mblk_t *
myri10ge_poll_rx(void * arg,int bytes)265810253Sxiuyan.wang@Sun.COM myri10ge_poll_rx(void *arg, int bytes)
265910253Sxiuyan.wang@Sun.COM {
266010253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss = arg;
266110253Sxiuyan.wang@Sun.COM 	struct myri10ge_mblk_list mbl;
266210253Sxiuyan.wang@Sun.COM 	boolean_t dummy = B_FALSE;
266310253Sxiuyan.wang@Sun.COM 
266410253Sxiuyan.wang@Sun.COM 	if (bytes == 0)
266510253Sxiuyan.wang@Sun.COM 		return (NULL);
266610253Sxiuyan.wang@Sun.COM 
266710253Sxiuyan.wang@Sun.COM 	myri10ge_mbl_init(&mbl);
266810253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->rx_lock);
266910253Sxiuyan.wang@Sun.COM 	if (ss->rx_polling)
267010253Sxiuyan.wang@Sun.COM 		myri10ge_clean_rx_done(ss, &mbl, bytes, &dummy);
267110253Sxiuyan.wang@Sun.COM 	else
267210253Sxiuyan.wang@Sun.COM 		printf("%d: poll_rx: token=%d, polling=%d\n", (int)(ss -
267310253Sxiuyan.wang@Sun.COM 		    ss->mgp->ss), ss->rx_token, ss->rx_polling);
267410253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->rx_lock);
267510253Sxiuyan.wang@Sun.COM 	return (mbl.head);
267610253Sxiuyan.wang@Sun.COM }
267710253Sxiuyan.wang@Sun.COM 
267810253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
267910253Sxiuyan.wang@Sun.COM static uint_t
myri10ge_intr(caddr_t arg0,caddr_t arg1)268010253Sxiuyan.wang@Sun.COM myri10ge_intr(caddr_t arg0, caddr_t arg1)
268110253Sxiuyan.wang@Sun.COM {
268210253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss =
268310253Sxiuyan.wang@Sun.COM 	    (struct myri10ge_slice_state *)(void *)arg0;
268410253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
268510253Sxiuyan.wang@Sun.COM 	mcp_irq_data_t *stats = ss->fw_stats;
268610253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
268710253Sxiuyan.wang@Sun.COM 	uint32_t send_done_count;
268810253Sxiuyan.wang@Sun.COM 	uint8_t valid;
268910253Sxiuyan.wang@Sun.COM 
269010253Sxiuyan.wang@Sun.COM 
269110253Sxiuyan.wang@Sun.COM 	/* make sure the DMA has finished */
269210253Sxiuyan.wang@Sun.COM 	if (!stats->valid) {
269310253Sxiuyan.wang@Sun.COM 		return (DDI_INTR_UNCLAIMED);
269410253Sxiuyan.wang@Sun.COM 	}
269510253Sxiuyan.wang@Sun.COM 	valid = stats->valid;
269610253Sxiuyan.wang@Sun.COM 
269710253Sxiuyan.wang@Sun.COM 	/* low bit indicates receives are present */
269810253Sxiuyan.wang@Sun.COM 	if (valid & 1)
269910253Sxiuyan.wang@Sun.COM 		myri10ge_intr_rx(ss);
270010253Sxiuyan.wang@Sun.COM 
270110253Sxiuyan.wang@Sun.COM 	if (mgp->ddi_intr_type == DDI_INTR_TYPE_FIXED) {
270210253Sxiuyan.wang@Sun.COM 		/* lower legacy IRQ  */
270310253Sxiuyan.wang@Sun.COM 		*mgp->irq_deassert = 0;
270410253Sxiuyan.wang@Sun.COM 		if (!myri10ge_deassert_wait)
270510253Sxiuyan.wang@Sun.COM 			/* don't wait for conf. that irq is low */
270610253Sxiuyan.wang@Sun.COM 			stats->valid = 0;
270710253Sxiuyan.wang@Sun.COM 		mb();
270810253Sxiuyan.wang@Sun.COM 	} else {
270910253Sxiuyan.wang@Sun.COM 		/* no need to wait for conf. that irq is low */
271010253Sxiuyan.wang@Sun.COM 		stats->valid = 0;
271110253Sxiuyan.wang@Sun.COM 	}
271210253Sxiuyan.wang@Sun.COM 
271310253Sxiuyan.wang@Sun.COM 	do {
271410253Sxiuyan.wang@Sun.COM 		/* check for transmit completes and receives */
271510253Sxiuyan.wang@Sun.COM 		send_done_count = ntohl(stats->send_done_count);
271610253Sxiuyan.wang@Sun.COM 		if (send_done_count != tx->pkt_done)
271710253Sxiuyan.wang@Sun.COM 			myri10ge_tx_done(ss, (int)send_done_count);
271810253Sxiuyan.wang@Sun.COM 	} while (*((volatile uint8_t *) &stats->valid));
271910253Sxiuyan.wang@Sun.COM 
272010253Sxiuyan.wang@Sun.COM 	if (stats->stats_updated) {
272110253Sxiuyan.wang@Sun.COM 		if (mgp->link_state != stats->link_up || stats->link_down) {
272210253Sxiuyan.wang@Sun.COM 			mgp->link_state = stats->link_up;
272310253Sxiuyan.wang@Sun.COM 			if (stats->link_down) {
272410253Sxiuyan.wang@Sun.COM 				mgp->down_cnt += stats->link_down;
272510253Sxiuyan.wang@Sun.COM 				mgp->link_state = 0;
272610253Sxiuyan.wang@Sun.COM 			}
272710253Sxiuyan.wang@Sun.COM 			if (mgp->link_state) {
272810253Sxiuyan.wang@Sun.COM 				if (myri10ge_verbose)
272910253Sxiuyan.wang@Sun.COM 					printf("%s: link up\n", mgp->name);
273010253Sxiuyan.wang@Sun.COM 				mac_link_update(mgp->mh, LINK_STATE_UP);
273110253Sxiuyan.wang@Sun.COM 			} else {
273210253Sxiuyan.wang@Sun.COM 				if (myri10ge_verbose)
273310253Sxiuyan.wang@Sun.COM 					printf("%s: link down\n", mgp->name);
273410253Sxiuyan.wang@Sun.COM 				mac_link_update(mgp->mh, LINK_STATE_DOWN);
273510253Sxiuyan.wang@Sun.COM 			}
273610253Sxiuyan.wang@Sun.COM 			MYRI10GE_NIC_STAT_INC(link_changes);
273710253Sxiuyan.wang@Sun.COM 		}
273810253Sxiuyan.wang@Sun.COM 		if (mgp->rdma_tags_available !=
273910253Sxiuyan.wang@Sun.COM 		    ntohl(ss->fw_stats->rdma_tags_available)) {
274010253Sxiuyan.wang@Sun.COM 			mgp->rdma_tags_available =
274110253Sxiuyan.wang@Sun.COM 			    ntohl(ss->fw_stats->rdma_tags_available);
274210253Sxiuyan.wang@Sun.COM 			cmn_err(CE_NOTE, "%s: RDMA timed out! "
274310253Sxiuyan.wang@Sun.COM 			    "%d tags left\n", mgp->name,
274410253Sxiuyan.wang@Sun.COM 			    mgp->rdma_tags_available);
274510253Sxiuyan.wang@Sun.COM 		}
274610253Sxiuyan.wang@Sun.COM 	}
274710253Sxiuyan.wang@Sun.COM 
274810253Sxiuyan.wang@Sun.COM 	mb();
274910253Sxiuyan.wang@Sun.COM 	/* check to see if we have rx token to pass back */
275010253Sxiuyan.wang@Sun.COM 	if (valid & 0x1) {
275110253Sxiuyan.wang@Sun.COM 		mutex_enter(&ss->poll_lock);
275210253Sxiuyan.wang@Sun.COM 		if (ss->rx_polling) {
275310253Sxiuyan.wang@Sun.COM 			ss->rx_token = 1;
275410253Sxiuyan.wang@Sun.COM 		} else {
275510253Sxiuyan.wang@Sun.COM 			*ss->irq_claim = BE_32(3);
275610253Sxiuyan.wang@Sun.COM 			ss->rx_token = 0;
275710253Sxiuyan.wang@Sun.COM 		}
275810253Sxiuyan.wang@Sun.COM 		mutex_exit(&ss->poll_lock);
275910253Sxiuyan.wang@Sun.COM 	}
276010253Sxiuyan.wang@Sun.COM 	*(ss->irq_claim + 1) = BE_32(3);
276110253Sxiuyan.wang@Sun.COM 	return (DDI_INTR_CLAIMED);
276210253Sxiuyan.wang@Sun.COM }
276310253Sxiuyan.wang@Sun.COM 
276410253Sxiuyan.wang@Sun.COM /*
276510253Sxiuyan.wang@Sun.COM  * Add or remove a multicast address.  This is called with our
276610253Sxiuyan.wang@Sun.COM  * macinfo's lock held by GLD, so we do not need to worry about
276710253Sxiuyan.wang@Sun.COM  * our own locking here.
276810253Sxiuyan.wang@Sun.COM  */
276910253Sxiuyan.wang@Sun.COM static int
myri10ge_m_multicst(void * arg,boolean_t add,const uint8_t * multicastaddr)277010253Sxiuyan.wang@Sun.COM myri10ge_m_multicst(void *arg, boolean_t add, const uint8_t *multicastaddr)
277110253Sxiuyan.wang@Sun.COM {
277210253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
277310253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
277410253Sxiuyan.wang@Sun.COM 	int status, join_leave;
277510253Sxiuyan.wang@Sun.COM 
277610253Sxiuyan.wang@Sun.COM 	if (add)
277710253Sxiuyan.wang@Sun.COM 		join_leave = MXGEFW_JOIN_MULTICAST_GROUP;
277810253Sxiuyan.wang@Sun.COM 	else
277910253Sxiuyan.wang@Sun.COM 		join_leave = MXGEFW_LEAVE_MULTICAST_GROUP;
278010253Sxiuyan.wang@Sun.COM 	(void) memcpy(&cmd.data0, multicastaddr, 4);
278110253Sxiuyan.wang@Sun.COM 	(void) memcpy(&cmd.data1, multicastaddr + 4, 2);
278210253Sxiuyan.wang@Sun.COM 	cmd.data0 = htonl(cmd.data0);
278310253Sxiuyan.wang@Sun.COM 	cmd.data1 = htonl(cmd.data1);
278410253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, join_leave, &cmd);
278510253Sxiuyan.wang@Sun.COM 	if (status == 0)
278610253Sxiuyan.wang@Sun.COM 		return (0);
278710253Sxiuyan.wang@Sun.COM 
278810253Sxiuyan.wang@Sun.COM 	cmn_err(CE_WARN, "%s: failed to set multicast address\n",
278910253Sxiuyan.wang@Sun.COM 	    mgp->name);
279010253Sxiuyan.wang@Sun.COM 	return (status);
279110253Sxiuyan.wang@Sun.COM }
279210253Sxiuyan.wang@Sun.COM 
279310253Sxiuyan.wang@Sun.COM 
279410253Sxiuyan.wang@Sun.COM static int
myri10ge_m_promisc(void * arg,boolean_t on)279510253Sxiuyan.wang@Sun.COM myri10ge_m_promisc(void *arg, boolean_t on)
279610253Sxiuyan.wang@Sun.COM {
279710253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
279810253Sxiuyan.wang@Sun.COM 
279910253Sxiuyan.wang@Sun.COM 	myri10ge_change_promisc(mgp, on);
280010253Sxiuyan.wang@Sun.COM 	return (0);
280110253Sxiuyan.wang@Sun.COM }
280210253Sxiuyan.wang@Sun.COM 
280310253Sxiuyan.wang@Sun.COM /*
280410253Sxiuyan.wang@Sun.COM  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
280510253Sxiuyan.wang@Sun.COM  *  backwards one at a time and handle ring wraps
280610253Sxiuyan.wang@Sun.COM  */
280710253Sxiuyan.wang@Sun.COM 
280810253Sxiuyan.wang@Sun.COM static inline void
myri10ge_submit_req_backwards(myri10ge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)280910253Sxiuyan.wang@Sun.COM myri10ge_submit_req_backwards(myri10ge_tx_ring_t *tx,
281010253Sxiuyan.wang@Sun.COM     mcp_kreq_ether_send_t *src, int cnt)
281110253Sxiuyan.wang@Sun.COM {
281210253Sxiuyan.wang@Sun.COM 	int idx, starting_slot;
281310253Sxiuyan.wang@Sun.COM 	starting_slot = tx->req;
281410253Sxiuyan.wang@Sun.COM 	while (cnt > 1) {
281510253Sxiuyan.wang@Sun.COM 		cnt--;
281610253Sxiuyan.wang@Sun.COM 		idx = (starting_slot + cnt) & tx->mask;
281710253Sxiuyan.wang@Sun.COM 		myri10ge_pio_copy(&tx->lanai[idx],
281810253Sxiuyan.wang@Sun.COM 		    &src[cnt], sizeof (*src));
281910253Sxiuyan.wang@Sun.COM 		mb();
282010253Sxiuyan.wang@Sun.COM 	}
282110253Sxiuyan.wang@Sun.COM }
282210253Sxiuyan.wang@Sun.COM 
282310253Sxiuyan.wang@Sun.COM /*
282410253Sxiuyan.wang@Sun.COM  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
282510253Sxiuyan.wang@Sun.COM  * at most 32 bytes at a time, so as to avoid involving the software
282610253Sxiuyan.wang@Sun.COM  * pio handler in the nic.   We re-write the first segment's flags
282710253Sxiuyan.wang@Sun.COM  * to mark them valid only after writing the entire chain
282810253Sxiuyan.wang@Sun.COM  */
282910253Sxiuyan.wang@Sun.COM 
283010253Sxiuyan.wang@Sun.COM static inline void
myri10ge_submit_req(myri10ge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)283110253Sxiuyan.wang@Sun.COM myri10ge_submit_req(myri10ge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
283210253Sxiuyan.wang@Sun.COM     int cnt)
283310253Sxiuyan.wang@Sun.COM {
283410253Sxiuyan.wang@Sun.COM 	int idx, i;
283510253Sxiuyan.wang@Sun.COM 	uint32_t *src_ints, *dst_ints;
283610253Sxiuyan.wang@Sun.COM 	mcp_kreq_ether_send_t *srcp, *dstp, *dst;
283710253Sxiuyan.wang@Sun.COM 	uint8_t last_flags;
283810253Sxiuyan.wang@Sun.COM 
283910253Sxiuyan.wang@Sun.COM 	idx = tx->req & tx->mask;
284010253Sxiuyan.wang@Sun.COM 
284110253Sxiuyan.wang@Sun.COM 	last_flags = src->flags;
284210253Sxiuyan.wang@Sun.COM 	src->flags = 0;
284310253Sxiuyan.wang@Sun.COM 	mb();
284410253Sxiuyan.wang@Sun.COM 	dst = dstp = &tx->lanai[idx];
284510253Sxiuyan.wang@Sun.COM 	srcp = src;
284610253Sxiuyan.wang@Sun.COM 
284710253Sxiuyan.wang@Sun.COM 	if ((idx + cnt) < tx->mask) {
284810253Sxiuyan.wang@Sun.COM 		for (i = 0; i < (cnt - 1); i += 2) {
284910253Sxiuyan.wang@Sun.COM 			myri10ge_pio_copy(dstp, srcp, 2 * sizeof (*src));
285010253Sxiuyan.wang@Sun.COM 			mb(); /* force write every 32 bytes */
285110253Sxiuyan.wang@Sun.COM 			srcp += 2;
285210253Sxiuyan.wang@Sun.COM 			dstp += 2;
285310253Sxiuyan.wang@Sun.COM 		}
285410253Sxiuyan.wang@Sun.COM 	} else {
285510253Sxiuyan.wang@Sun.COM 		/*
285610253Sxiuyan.wang@Sun.COM 		 * submit all but the first request, and ensure
285710253Sxiuyan.wang@Sun.COM 		 *  that it is submitted below
285810253Sxiuyan.wang@Sun.COM 		 */
285910253Sxiuyan.wang@Sun.COM 		myri10ge_submit_req_backwards(tx, src, cnt);
286010253Sxiuyan.wang@Sun.COM 		i = 0;
286110253Sxiuyan.wang@Sun.COM 	}
286210253Sxiuyan.wang@Sun.COM 	if (i < cnt) {
286310253Sxiuyan.wang@Sun.COM 		/* submit the first request */
286410253Sxiuyan.wang@Sun.COM 		myri10ge_pio_copy(dstp, srcp, sizeof (*src));
286510253Sxiuyan.wang@Sun.COM 		mb(); /* barrier before setting valid flag */
286610253Sxiuyan.wang@Sun.COM 	}
286710253Sxiuyan.wang@Sun.COM 
286810253Sxiuyan.wang@Sun.COM 	/* re-write the last 32-bits with the valid flags */
286910253Sxiuyan.wang@Sun.COM 	src->flags |= last_flags;
287010253Sxiuyan.wang@Sun.COM 	src_ints = (uint32_t *)src;
287110253Sxiuyan.wang@Sun.COM 	src_ints += 3;
287210253Sxiuyan.wang@Sun.COM 	dst_ints = (uint32_t *)dst;
287310253Sxiuyan.wang@Sun.COM 	dst_ints += 3;
287410253Sxiuyan.wang@Sun.COM 	*dst_ints =  *src_ints;
287510253Sxiuyan.wang@Sun.COM 	tx->req += cnt;
287610253Sxiuyan.wang@Sun.COM 	mb();
287710253Sxiuyan.wang@Sun.COM 	/* notify NIC to poll this tx ring */
287810253Sxiuyan.wang@Sun.COM 	if (!tx->active && tx->go != NULL) {
287910253Sxiuyan.wang@Sun.COM 		*(int *)(void *)tx->go = 1;
288010253Sxiuyan.wang@Sun.COM 		tx->active = 1;
288110253Sxiuyan.wang@Sun.COM 		tx->activate++;
288210253Sxiuyan.wang@Sun.COM 		mb();
288310253Sxiuyan.wang@Sun.COM 	}
288410253Sxiuyan.wang@Sun.COM }
288510253Sxiuyan.wang@Sun.COM 
288610253Sxiuyan.wang@Sun.COM /* ARGSUSED */
288710253Sxiuyan.wang@Sun.COM static inline void
myri10ge_lso_info_get(mblk_t * mp,uint32_t * mss,uint32_t * flags)288810253Sxiuyan.wang@Sun.COM myri10ge_lso_info_get(mblk_t *mp, uint32_t *mss, uint32_t *flags)
288910253Sxiuyan.wang@Sun.COM {
289010253Sxiuyan.wang@Sun.COM 	uint32_t lso_flag;
2891*11878SVenu.Iyer@Sun.COM 	mac_lso_get(mp, mss, &lso_flag);
289210253Sxiuyan.wang@Sun.COM 	(*flags) |= lso_flag;
289310253Sxiuyan.wang@Sun.COM }
289410253Sxiuyan.wang@Sun.COM 
289510253Sxiuyan.wang@Sun.COM 
289610253Sxiuyan.wang@Sun.COM /* like pullupmsg, except preserve hcksum/LSO attributes */
289710253Sxiuyan.wang@Sun.COM static int
myri10ge_pullup(struct myri10ge_slice_state * ss,mblk_t * mp)289810253Sxiuyan.wang@Sun.COM myri10ge_pullup(struct myri10ge_slice_state *ss, mblk_t *mp)
289910253Sxiuyan.wang@Sun.COM {
290010253Sxiuyan.wang@Sun.COM 	uint32_t start, stuff, tx_offload_flags, mss;
290110253Sxiuyan.wang@Sun.COM 	int ok;
290210253Sxiuyan.wang@Sun.COM 
290310253Sxiuyan.wang@Sun.COM 	mss = 0;
2904*11878SVenu.Iyer@Sun.COM 	mac_hcksum_get(mp, &start, &stuff, NULL, NULL, &tx_offload_flags);
290510253Sxiuyan.wang@Sun.COM 	myri10ge_lso_info_get(mp, &mss, &tx_offload_flags);
290610253Sxiuyan.wang@Sun.COM 
290710253Sxiuyan.wang@Sun.COM 	ok = pullupmsg(mp, -1);
290810253Sxiuyan.wang@Sun.COM 	if (!ok) {
290910253Sxiuyan.wang@Sun.COM 		printf("pullupmsg failed");
291010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
291110253Sxiuyan.wang@Sun.COM 	}
291210253Sxiuyan.wang@Sun.COM 	MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_pullup);
2913*11878SVenu.Iyer@Sun.COM 	mac_hcksum_set(mp, start, stuff, NULL, NULL, tx_offload_flags);
291410253Sxiuyan.wang@Sun.COM 	if (tx_offload_flags & HW_LSO)
291510253Sxiuyan.wang@Sun.COM 		DB_LSOMSS(mp) = (uint16_t)mss;
291610253Sxiuyan.wang@Sun.COM 	lso_info_set(mp, mss, tx_offload_flags);
291710253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
291810253Sxiuyan.wang@Sun.COM }
291910253Sxiuyan.wang@Sun.COM 
292010253Sxiuyan.wang@Sun.COM static inline void
myri10ge_tx_stat(struct myri10ge_tx_pkt_stats * s,struct ether_header * eh,int opackets,int obytes)292110253Sxiuyan.wang@Sun.COM myri10ge_tx_stat(struct myri10ge_tx_pkt_stats *s, struct ether_header *eh,
292210253Sxiuyan.wang@Sun.COM     int opackets, int obytes)
292310253Sxiuyan.wang@Sun.COM {
292410253Sxiuyan.wang@Sun.COM 	s->un.all = 0;
292510253Sxiuyan.wang@Sun.COM 	if (eh->ether_dhost.ether_addr_octet[0] & 1) {
292610253Sxiuyan.wang@Sun.COM 		if (0 == (bcmp(eh->ether_dhost.ether_addr_octet,
292710253Sxiuyan.wang@Sun.COM 		    myri10ge_broadcastaddr, sizeof (eh->ether_dhost))))
292810253Sxiuyan.wang@Sun.COM 			s->un.s.brdcstxmt = 1;
292910253Sxiuyan.wang@Sun.COM 		else
293010253Sxiuyan.wang@Sun.COM 			s->un.s.multixmt = 1;
293110253Sxiuyan.wang@Sun.COM 	}
293210253Sxiuyan.wang@Sun.COM 	s->un.s.opackets = (uint16_t)opackets;
293310253Sxiuyan.wang@Sun.COM 	s->un.s.obytes = obytes;
293410253Sxiuyan.wang@Sun.COM }
293510253Sxiuyan.wang@Sun.COM 
293610253Sxiuyan.wang@Sun.COM static int
myri10ge_tx_copy(struct myri10ge_slice_state * ss,mblk_t * mp,mcp_kreq_ether_send_t * req)293710253Sxiuyan.wang@Sun.COM myri10ge_tx_copy(struct myri10ge_slice_state *ss, mblk_t *mp,
293810253Sxiuyan.wang@Sun.COM     mcp_kreq_ether_send_t *req)
293910253Sxiuyan.wang@Sun.COM {
294010253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
294110253Sxiuyan.wang@Sun.COM 	caddr_t ptr;
294210253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_copybuf *cp;
294310253Sxiuyan.wang@Sun.COM 	mblk_t *bp;
294410253Sxiuyan.wang@Sun.COM 	int idx, mblen, avail;
294510253Sxiuyan.wang@Sun.COM 	uint16_t len;
294610253Sxiuyan.wang@Sun.COM 
294710253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->lock);
294810253Sxiuyan.wang@Sun.COM 	avail = tx->mask - (tx->req - tx->done);
294910253Sxiuyan.wang@Sun.COM 	if (avail <= 1) {
295010253Sxiuyan.wang@Sun.COM 		mutex_exit(&tx->lock);
295110253Sxiuyan.wang@Sun.COM 		return (EBUSY);
295210253Sxiuyan.wang@Sun.COM 	}
295310253Sxiuyan.wang@Sun.COM 	idx = tx->req & tx->mask;
295410253Sxiuyan.wang@Sun.COM 	cp = &tx->cp[idx];
295510253Sxiuyan.wang@Sun.COM 	ptr = cp->va;
295610253Sxiuyan.wang@Sun.COM 	for (len = 0, bp = mp; bp != NULL; bp = bp->b_cont) {
295710253Sxiuyan.wang@Sun.COM 		mblen = MBLKL(bp);
295810253Sxiuyan.wang@Sun.COM 		bcopy(bp->b_rptr, ptr, mblen);
295910253Sxiuyan.wang@Sun.COM 		ptr += mblen;
296010253Sxiuyan.wang@Sun.COM 		len += mblen;
296110253Sxiuyan.wang@Sun.COM 	}
296210253Sxiuyan.wang@Sun.COM 	/* ensure runts are padded to 60 bytes */
296310253Sxiuyan.wang@Sun.COM 	if (len < 60) {
296410253Sxiuyan.wang@Sun.COM 		bzero(ptr, 64 - len);
296510253Sxiuyan.wang@Sun.COM 		len = 60;
296610253Sxiuyan.wang@Sun.COM 	}
296710253Sxiuyan.wang@Sun.COM 	req->addr_low = cp->dma.low;
296810253Sxiuyan.wang@Sun.COM 	req->addr_high = cp->dma.high;
296910253Sxiuyan.wang@Sun.COM 	req->length = htons(len);
297010253Sxiuyan.wang@Sun.COM 	req->pad = 0;
297110253Sxiuyan.wang@Sun.COM 	req->rdma_count = 1;
297210253Sxiuyan.wang@Sun.COM 	myri10ge_tx_stat(&tx->info[idx].stat,
297310253Sxiuyan.wang@Sun.COM 	    (struct ether_header *)(void *)cp->va, 1, len);
297410253Sxiuyan.wang@Sun.COM 	(void) ddi_dma_sync(cp->dma.handle, 0, len, DDI_DMA_SYNC_FORDEV);
297510253Sxiuyan.wang@Sun.COM 	myri10ge_submit_req(&ss->tx, req, 1);
297610253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->lock);
297710253Sxiuyan.wang@Sun.COM 	freemsg(mp);
297810253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
297910253Sxiuyan.wang@Sun.COM }
298010253Sxiuyan.wang@Sun.COM 
298110253Sxiuyan.wang@Sun.COM 
298210253Sxiuyan.wang@Sun.COM static void
myri10ge_send_locked(myri10ge_tx_ring_t * tx,mcp_kreq_ether_send_t * req_list,struct myri10ge_tx_buffer_state * tx_info,int count)298310253Sxiuyan.wang@Sun.COM myri10ge_send_locked(myri10ge_tx_ring_t *tx, mcp_kreq_ether_send_t *req_list,
298410253Sxiuyan.wang@Sun.COM     struct myri10ge_tx_buffer_state *tx_info,
298510253Sxiuyan.wang@Sun.COM     int count)
298610253Sxiuyan.wang@Sun.COM {
298710253Sxiuyan.wang@Sun.COM 	int i, idx;
298810253Sxiuyan.wang@Sun.COM 
298910253Sxiuyan.wang@Sun.COM 	idx = 0; /* gcc -Wuninitialized */
299010253Sxiuyan.wang@Sun.COM 	/* store unmapping and bp info for tx irq handler */
299110253Sxiuyan.wang@Sun.COM 	for (i = 0; i < count; i++) {
299210253Sxiuyan.wang@Sun.COM 		idx = (tx->req + i) & tx->mask;
299310253Sxiuyan.wang@Sun.COM 		tx->info[idx].m = tx_info[i].m;
299410253Sxiuyan.wang@Sun.COM 		tx->info[idx].handle = tx_info[i].handle;
299510253Sxiuyan.wang@Sun.COM 	}
299610253Sxiuyan.wang@Sun.COM 	tx->info[idx].stat.un.all = tx_info[0].stat.un.all;
299710253Sxiuyan.wang@Sun.COM 
299810253Sxiuyan.wang@Sun.COM 	/* submit the frame to the nic */
299910253Sxiuyan.wang@Sun.COM 	myri10ge_submit_req(tx, req_list, count);
300010253Sxiuyan.wang@Sun.COM 
300110253Sxiuyan.wang@Sun.COM 
300210253Sxiuyan.wang@Sun.COM }
300310253Sxiuyan.wang@Sun.COM 
300410253Sxiuyan.wang@Sun.COM 
300510253Sxiuyan.wang@Sun.COM 
300610253Sxiuyan.wang@Sun.COM static void
myri10ge_copydata(mblk_t * mp,int off,int len,caddr_t buf)300710253Sxiuyan.wang@Sun.COM myri10ge_copydata(mblk_t *mp, int off, int len, caddr_t buf)
300810253Sxiuyan.wang@Sun.COM {
300910253Sxiuyan.wang@Sun.COM 	mblk_t *bp;
301010253Sxiuyan.wang@Sun.COM 	int seglen;
301110253Sxiuyan.wang@Sun.COM 	uint_t count;
301210253Sxiuyan.wang@Sun.COM 
301310253Sxiuyan.wang@Sun.COM 	bp = mp;
301410253Sxiuyan.wang@Sun.COM 
301510253Sxiuyan.wang@Sun.COM 	while (off > 0) {
301610253Sxiuyan.wang@Sun.COM 		seglen = MBLKL(bp);
301710253Sxiuyan.wang@Sun.COM 		if (off < seglen)
301810253Sxiuyan.wang@Sun.COM 			break;
301910253Sxiuyan.wang@Sun.COM 		off -= seglen;
302010253Sxiuyan.wang@Sun.COM 		bp = bp->b_cont;
302110253Sxiuyan.wang@Sun.COM 	}
302210253Sxiuyan.wang@Sun.COM 	while (len > 0) {
302310253Sxiuyan.wang@Sun.COM 		seglen = MBLKL(bp);
302410253Sxiuyan.wang@Sun.COM 		count = min(seglen - off, len);
302510253Sxiuyan.wang@Sun.COM 		bcopy(bp->b_rptr + off, buf, count);
302610253Sxiuyan.wang@Sun.COM 		len -= count;
302710253Sxiuyan.wang@Sun.COM 		buf += count;
302810253Sxiuyan.wang@Sun.COM 		off = 0;
302910253Sxiuyan.wang@Sun.COM 		bp = bp->b_cont;
303010253Sxiuyan.wang@Sun.COM 	}
303110253Sxiuyan.wang@Sun.COM }
303210253Sxiuyan.wang@Sun.COM 
303310253Sxiuyan.wang@Sun.COM static int
myri10ge_ether_parse_header(mblk_t * mp)303410253Sxiuyan.wang@Sun.COM myri10ge_ether_parse_header(mblk_t *mp)
303510253Sxiuyan.wang@Sun.COM {
303610253Sxiuyan.wang@Sun.COM 	struct ether_header eh_copy;
303710253Sxiuyan.wang@Sun.COM 	struct ether_header *eh;
303810253Sxiuyan.wang@Sun.COM 	int eth_hdr_len, seglen;
303910253Sxiuyan.wang@Sun.COM 
304010253Sxiuyan.wang@Sun.COM 	seglen = MBLKL(mp);
304110253Sxiuyan.wang@Sun.COM 	eth_hdr_len = sizeof (*eh);
304210253Sxiuyan.wang@Sun.COM 	if (seglen < eth_hdr_len) {
304310253Sxiuyan.wang@Sun.COM 		myri10ge_copydata(mp, 0, eth_hdr_len, (caddr_t)&eh_copy);
304410253Sxiuyan.wang@Sun.COM 		eh = &eh_copy;
304510253Sxiuyan.wang@Sun.COM 	} else {
304610253Sxiuyan.wang@Sun.COM 		eh = (struct ether_header *)(void *)mp->b_rptr;
304710253Sxiuyan.wang@Sun.COM 	}
304810253Sxiuyan.wang@Sun.COM 	if (eh->ether_type == BE_16(ETHERTYPE_VLAN)) {
304910253Sxiuyan.wang@Sun.COM 		eth_hdr_len += 4;
305010253Sxiuyan.wang@Sun.COM 	}
305110253Sxiuyan.wang@Sun.COM 
305210253Sxiuyan.wang@Sun.COM 	return (eth_hdr_len);
305310253Sxiuyan.wang@Sun.COM }
305410253Sxiuyan.wang@Sun.COM 
305510253Sxiuyan.wang@Sun.COM static int
myri10ge_lso_parse_header(mblk_t * mp,int off)305610253Sxiuyan.wang@Sun.COM myri10ge_lso_parse_header(mblk_t *mp, int off)
305710253Sxiuyan.wang@Sun.COM {
305810253Sxiuyan.wang@Sun.COM 	char buf[128];
305911652Sxiuyan.wang@Sun.COM 	int seglen, sum_off;
306010253Sxiuyan.wang@Sun.COM 	struct ip *ip;
306110253Sxiuyan.wang@Sun.COM 	struct tcphdr *tcp;
306210253Sxiuyan.wang@Sun.COM 
306310253Sxiuyan.wang@Sun.COM 	seglen = MBLKL(mp);
306410253Sxiuyan.wang@Sun.COM 	if (seglen < off + sizeof (*ip)) {
306510253Sxiuyan.wang@Sun.COM 		myri10ge_copydata(mp, off, sizeof (*ip), buf);
306610253Sxiuyan.wang@Sun.COM 		ip = (struct ip *)(void *)buf;
306710253Sxiuyan.wang@Sun.COM 	} else {
306810253Sxiuyan.wang@Sun.COM 		ip = (struct ip *)(void *)(mp->b_rptr + off);
306910253Sxiuyan.wang@Sun.COM 	}
307010253Sxiuyan.wang@Sun.COM 	if (seglen < off + (ip->ip_hl << 2) + sizeof (*tcp)) {
307110253Sxiuyan.wang@Sun.COM 		myri10ge_copydata(mp, off,
307210253Sxiuyan.wang@Sun.COM 		    (ip->ip_hl << 2) + sizeof (*tcp), buf);
307310253Sxiuyan.wang@Sun.COM 		ip = (struct ip *)(void *)buf;
307410253Sxiuyan.wang@Sun.COM 	}
307510253Sxiuyan.wang@Sun.COM 	tcp = (struct tcphdr *)(void *)((char *)ip + (ip->ip_hl << 2));
307611652Sxiuyan.wang@Sun.COM 
307711652Sxiuyan.wang@Sun.COM 	/*
307811652Sxiuyan.wang@Sun.COM 	 * NIC expects ip_sum to be zero.  Recent changes to
307911652Sxiuyan.wang@Sun.COM 	 * OpenSolaris leave the correct ip checksum there, rather
308011652Sxiuyan.wang@Sun.COM 	 * than the required zero, so we need to zero it.  Otherwise,
308111652Sxiuyan.wang@Sun.COM 	 * the NIC will produce bad checksums when sending LSO packets.
308211652Sxiuyan.wang@Sun.COM 	 */
308311652Sxiuyan.wang@Sun.COM 	if (ip->ip_sum != 0) {
308411652Sxiuyan.wang@Sun.COM 		if (((char *)ip) != buf) {
308511652Sxiuyan.wang@Sun.COM 			/* ip points into mblk, so just zero it */
308611652Sxiuyan.wang@Sun.COM 			ip->ip_sum = 0;
308711652Sxiuyan.wang@Sun.COM 		} else {
308811652Sxiuyan.wang@Sun.COM 			/*
308911652Sxiuyan.wang@Sun.COM 			 * ip points into a copy, so walk the chain
309011652Sxiuyan.wang@Sun.COM 			 * to find the ip_csum, then zero it
309111652Sxiuyan.wang@Sun.COM 			 */
309211652Sxiuyan.wang@Sun.COM 			sum_off = off + _PTRDIFF(&ip->ip_sum, buf);
309311652Sxiuyan.wang@Sun.COM 			while (sum_off > (int)(MBLKL(mp) - 1)) {
309411652Sxiuyan.wang@Sun.COM 				sum_off -= MBLKL(mp);
309511652Sxiuyan.wang@Sun.COM 				mp = mp->b_cont;
309611652Sxiuyan.wang@Sun.COM 			}
309711652Sxiuyan.wang@Sun.COM 			mp->b_rptr[sum_off] = 0;
309811652Sxiuyan.wang@Sun.COM 			sum_off++;
309911652Sxiuyan.wang@Sun.COM 			while (sum_off > MBLKL(mp) - 1) {
310011652Sxiuyan.wang@Sun.COM 				sum_off -= MBLKL(mp);
310111652Sxiuyan.wang@Sun.COM 				mp = mp->b_cont;
310211652Sxiuyan.wang@Sun.COM 			}
310311652Sxiuyan.wang@Sun.COM 			mp->b_rptr[sum_off] = 0;
310411652Sxiuyan.wang@Sun.COM 		}
310511652Sxiuyan.wang@Sun.COM 	}
310610253Sxiuyan.wang@Sun.COM 	return (off + ((ip->ip_hl + tcp->th_off) << 2));
310710253Sxiuyan.wang@Sun.COM }
310810253Sxiuyan.wang@Sun.COM 
310910253Sxiuyan.wang@Sun.COM static int
myri10ge_tx_tso_copy(struct myri10ge_slice_state * ss,mblk_t * mp,mcp_kreq_ether_send_t * req_list,int hdr_size,int pkt_size,uint16_t mss,uint8_t cksum_offset)311010253Sxiuyan.wang@Sun.COM myri10ge_tx_tso_copy(struct myri10ge_slice_state *ss, mblk_t *mp,
311110253Sxiuyan.wang@Sun.COM     mcp_kreq_ether_send_t *req_list, int hdr_size, int pkt_size,
311210253Sxiuyan.wang@Sun.COM     uint16_t mss, uint8_t cksum_offset)
311310253Sxiuyan.wang@Sun.COM {
311410253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
311510253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
311610253Sxiuyan.wang@Sun.COM 	mblk_t *bp;
311710253Sxiuyan.wang@Sun.COM 	mcp_kreq_ether_send_t *req;
311810253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_copybuf *cp;
311910253Sxiuyan.wang@Sun.COM 	caddr_t rptr, ptr;
312010253Sxiuyan.wang@Sun.COM 	int mblen, count, cum_len, mss_resid, tx_req, pkt_size_tmp;
312110253Sxiuyan.wang@Sun.COM 	int resid, avail, idx, hdr_size_tmp, tx_boundary;
312210253Sxiuyan.wang@Sun.COM 	int rdma_count;
312310253Sxiuyan.wang@Sun.COM 	uint32_t seglen, len, boundary, low, high_swapped;
312410253Sxiuyan.wang@Sun.COM 	uint16_t pseudo_hdr_offset = htons(mss);
312510253Sxiuyan.wang@Sun.COM 	uint8_t flags;
312610253Sxiuyan.wang@Sun.COM 
312710253Sxiuyan.wang@Sun.COM 	tx_boundary = mgp->tx_boundary;
312810253Sxiuyan.wang@Sun.COM 	hdr_size_tmp = hdr_size;
312910253Sxiuyan.wang@Sun.COM 	resid = tx_boundary;
313010253Sxiuyan.wang@Sun.COM 	count = 1;
313110253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->lock);
313210253Sxiuyan.wang@Sun.COM 
313310253Sxiuyan.wang@Sun.COM 	/* check to see if the slots are really there */
313410253Sxiuyan.wang@Sun.COM 	avail = tx->mask - (tx->req - tx->done);
313510253Sxiuyan.wang@Sun.COM 	if (unlikely(avail <=  MYRI10GE_MAX_SEND_DESC_TSO)) {
313610253Sxiuyan.wang@Sun.COM 		atomic_add_32(&tx->stall, 1);
313710253Sxiuyan.wang@Sun.COM 		mutex_exit(&tx->lock);
313810253Sxiuyan.wang@Sun.COM 		return (EBUSY);
313910253Sxiuyan.wang@Sun.COM 	}
314010253Sxiuyan.wang@Sun.COM 
314110253Sxiuyan.wang@Sun.COM 	/* copy */
314210253Sxiuyan.wang@Sun.COM 	cum_len = -hdr_size;
314310253Sxiuyan.wang@Sun.COM 	count = 0;
314410253Sxiuyan.wang@Sun.COM 	req = req_list;
314510253Sxiuyan.wang@Sun.COM 	idx = tx->mask & tx->req;
314610253Sxiuyan.wang@Sun.COM 	cp = &tx->cp[idx];
314710253Sxiuyan.wang@Sun.COM 	low = ntohl(cp->dma.low);
314810253Sxiuyan.wang@Sun.COM 	ptr = cp->va;
314910253Sxiuyan.wang@Sun.COM 	cp->len = 0;
315010253Sxiuyan.wang@Sun.COM 	if (mss) {
315110253Sxiuyan.wang@Sun.COM 		int payload = pkt_size - hdr_size;
315210253Sxiuyan.wang@Sun.COM 		uint16_t opackets = (payload / mss) + ((payload % mss) != 0);
315310253Sxiuyan.wang@Sun.COM 		tx->info[idx].ostat.opackets = opackets;
315410253Sxiuyan.wang@Sun.COM 		tx->info[idx].ostat.obytes = (opackets - 1) * hdr_size
315510253Sxiuyan.wang@Sun.COM 		    + pkt_size;
315610253Sxiuyan.wang@Sun.COM 	}
315710253Sxiuyan.wang@Sun.COM 	hdr_size_tmp = hdr_size;
315810253Sxiuyan.wang@Sun.COM 	mss_resid = mss;
315910253Sxiuyan.wang@Sun.COM 	flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST);
316010253Sxiuyan.wang@Sun.COM 	tx_req = tx->req;
316110253Sxiuyan.wang@Sun.COM 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
316210253Sxiuyan.wang@Sun.COM 		mblen = MBLKL(bp);
316310253Sxiuyan.wang@Sun.COM 		rptr = (caddr_t)bp->b_rptr;
316410253Sxiuyan.wang@Sun.COM 		len = min(hdr_size_tmp, mblen);
316510253Sxiuyan.wang@Sun.COM 		if (len) {
316610253Sxiuyan.wang@Sun.COM 			bcopy(rptr, ptr, len);
316710253Sxiuyan.wang@Sun.COM 			rptr += len;
316810253Sxiuyan.wang@Sun.COM 			ptr += len;
316910253Sxiuyan.wang@Sun.COM 			resid -= len;
317010253Sxiuyan.wang@Sun.COM 			mblen -= len;
317110253Sxiuyan.wang@Sun.COM 			hdr_size_tmp -= len;
317210253Sxiuyan.wang@Sun.COM 			cp->len += len;
317310253Sxiuyan.wang@Sun.COM 			if (hdr_size_tmp)
317410253Sxiuyan.wang@Sun.COM 				continue;
317510253Sxiuyan.wang@Sun.COM 			if (resid < mss) {
317610253Sxiuyan.wang@Sun.COM 				tx_req++;
317710253Sxiuyan.wang@Sun.COM 				idx = tx->mask & tx_req;
317810253Sxiuyan.wang@Sun.COM 				cp = &tx->cp[idx];
317910253Sxiuyan.wang@Sun.COM 				low = ntohl(cp->dma.low);
318010253Sxiuyan.wang@Sun.COM 				ptr = cp->va;
318110253Sxiuyan.wang@Sun.COM 				resid = tx_boundary;
318210253Sxiuyan.wang@Sun.COM 			}
318310253Sxiuyan.wang@Sun.COM 		}
318410253Sxiuyan.wang@Sun.COM 		while (mblen) {
318510253Sxiuyan.wang@Sun.COM 			len = min(mss_resid, mblen);
318610253Sxiuyan.wang@Sun.COM 			bcopy(rptr, ptr, len);
318710253Sxiuyan.wang@Sun.COM 			mss_resid -= len;
318810253Sxiuyan.wang@Sun.COM 			resid -= len;
318910253Sxiuyan.wang@Sun.COM 			mblen -= len;
319010253Sxiuyan.wang@Sun.COM 			rptr += len;
319110253Sxiuyan.wang@Sun.COM 			ptr += len;
319210253Sxiuyan.wang@Sun.COM 			cp->len += len;
319310253Sxiuyan.wang@Sun.COM 			if (mss_resid == 0) {
319410253Sxiuyan.wang@Sun.COM 				mss_resid = mss;
319510253Sxiuyan.wang@Sun.COM 				if (resid < mss) {
319610253Sxiuyan.wang@Sun.COM 					tx_req++;
319710253Sxiuyan.wang@Sun.COM 					idx = tx->mask & tx_req;
319810253Sxiuyan.wang@Sun.COM 					cp = &tx->cp[idx];
319910253Sxiuyan.wang@Sun.COM 					cp->len = 0;
320010253Sxiuyan.wang@Sun.COM 					low = ntohl(cp->dma.low);
320110253Sxiuyan.wang@Sun.COM 					ptr = cp->va;
320210253Sxiuyan.wang@Sun.COM 					resid = tx_boundary;
320310253Sxiuyan.wang@Sun.COM 				}
320410253Sxiuyan.wang@Sun.COM 			}
320510253Sxiuyan.wang@Sun.COM 		}
320610253Sxiuyan.wang@Sun.COM 	}
320710253Sxiuyan.wang@Sun.COM 
320810253Sxiuyan.wang@Sun.COM 	req = req_list;
320910253Sxiuyan.wang@Sun.COM 	pkt_size_tmp = pkt_size;
321010253Sxiuyan.wang@Sun.COM 	count = 0;
321110253Sxiuyan.wang@Sun.COM 	rdma_count = 0;
321210253Sxiuyan.wang@Sun.COM 	tx_req = tx->req;
321310253Sxiuyan.wang@Sun.COM 	while (pkt_size_tmp) {
321410253Sxiuyan.wang@Sun.COM 		idx = tx->mask & tx_req;
321510253Sxiuyan.wang@Sun.COM 		cp = &tx->cp[idx];
321610253Sxiuyan.wang@Sun.COM 		high_swapped = cp->dma.high;
321710253Sxiuyan.wang@Sun.COM 		low = ntohl(cp->dma.low);
321810253Sxiuyan.wang@Sun.COM 		len = cp->len;
321910253Sxiuyan.wang@Sun.COM 		if (len == 0) {
322010253Sxiuyan.wang@Sun.COM 			printf("len=0! pkt_size_tmp=%d, pkt_size=%d\n",
322110253Sxiuyan.wang@Sun.COM 			    pkt_size_tmp, pkt_size);
322210253Sxiuyan.wang@Sun.COM 			for (bp = mp; bp != NULL; bp = bp->b_cont) {
322310253Sxiuyan.wang@Sun.COM 				mblen = MBLKL(bp);
322410253Sxiuyan.wang@Sun.COM 				printf("mblen:%d\n", mblen);
322510253Sxiuyan.wang@Sun.COM 			}
322610253Sxiuyan.wang@Sun.COM 			pkt_size_tmp = pkt_size;
322710253Sxiuyan.wang@Sun.COM 			tx_req = tx->req;
322810253Sxiuyan.wang@Sun.COM 			while (pkt_size_tmp > 0) {
322910253Sxiuyan.wang@Sun.COM 				idx = tx->mask & tx_req;
323010253Sxiuyan.wang@Sun.COM 				cp = &tx->cp[idx];
323110253Sxiuyan.wang@Sun.COM 				printf("cp->len = %d\n", cp->len);
323210253Sxiuyan.wang@Sun.COM 				pkt_size_tmp -= cp->len;
323310253Sxiuyan.wang@Sun.COM 				tx_req++;
323410253Sxiuyan.wang@Sun.COM 			}
323510253Sxiuyan.wang@Sun.COM 			printf("dropped\n");
323610253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_err);
323710253Sxiuyan.wang@Sun.COM 			goto done;
323810253Sxiuyan.wang@Sun.COM 		}
323910253Sxiuyan.wang@Sun.COM 		pkt_size_tmp -= len;
324010253Sxiuyan.wang@Sun.COM 		while (len) {
324110253Sxiuyan.wang@Sun.COM 			while (len) {
324210253Sxiuyan.wang@Sun.COM 				uint8_t flags_next;
324310253Sxiuyan.wang@Sun.COM 				int cum_len_next;
324410253Sxiuyan.wang@Sun.COM 
324510253Sxiuyan.wang@Sun.COM 				boundary = (low + mgp->tx_boundary) &
324610253Sxiuyan.wang@Sun.COM 				    ~(mgp->tx_boundary - 1);
324710253Sxiuyan.wang@Sun.COM 				seglen = boundary - low;
324810253Sxiuyan.wang@Sun.COM 				if (seglen > len)
324910253Sxiuyan.wang@Sun.COM 					seglen = len;
325010253Sxiuyan.wang@Sun.COM 
325110253Sxiuyan.wang@Sun.COM 				flags_next = flags & ~MXGEFW_FLAGS_FIRST;
325210253Sxiuyan.wang@Sun.COM 				cum_len_next = cum_len + seglen;
325310253Sxiuyan.wang@Sun.COM 				(req-rdma_count)->rdma_count = rdma_count + 1;
325410253Sxiuyan.wang@Sun.COM 				if (likely(cum_len >= 0)) {
325510253Sxiuyan.wang@Sun.COM 					/* payload */
325610253Sxiuyan.wang@Sun.COM 					int next_is_first, chop;
325710253Sxiuyan.wang@Sun.COM 
325810253Sxiuyan.wang@Sun.COM 					chop = (cum_len_next > mss);
325910253Sxiuyan.wang@Sun.COM 					cum_len_next = cum_len_next % mss;
326010253Sxiuyan.wang@Sun.COM 					next_is_first = (cum_len_next == 0);
326110253Sxiuyan.wang@Sun.COM 					flags |= chop *
326210253Sxiuyan.wang@Sun.COM 					    MXGEFW_FLAGS_TSO_CHOP;
326310253Sxiuyan.wang@Sun.COM 					flags_next |= next_is_first *
326410253Sxiuyan.wang@Sun.COM 					    MXGEFW_FLAGS_FIRST;
326510253Sxiuyan.wang@Sun.COM 					rdma_count |= -(chop | next_is_first);
326610253Sxiuyan.wang@Sun.COM 					rdma_count += chop & !next_is_first;
326710253Sxiuyan.wang@Sun.COM 				} else if (likely(cum_len_next >= 0)) {
326810253Sxiuyan.wang@Sun.COM 					/* header ends */
326910253Sxiuyan.wang@Sun.COM 					int small;
327010253Sxiuyan.wang@Sun.COM 
327110253Sxiuyan.wang@Sun.COM 					rdma_count = -1;
327210253Sxiuyan.wang@Sun.COM 					cum_len_next = 0;
327310253Sxiuyan.wang@Sun.COM 					seglen = -cum_len;
327410253Sxiuyan.wang@Sun.COM 					small = (mss <= MXGEFW_SEND_SMALL_SIZE);
327510253Sxiuyan.wang@Sun.COM 					flags_next = MXGEFW_FLAGS_TSO_PLD |
327610253Sxiuyan.wang@Sun.COM 					    MXGEFW_FLAGS_FIRST |
327710253Sxiuyan.wang@Sun.COM 					    (small * MXGEFW_FLAGS_SMALL);
327810253Sxiuyan.wang@Sun.COM 				}
327910253Sxiuyan.wang@Sun.COM 				req->addr_high = high_swapped;
328010253Sxiuyan.wang@Sun.COM 				req->addr_low = htonl(low);
328110253Sxiuyan.wang@Sun.COM 				req->pseudo_hdr_offset = pseudo_hdr_offset;
328210253Sxiuyan.wang@Sun.COM 				req->pad = 0; /* complete solid 16-byte block */
328310253Sxiuyan.wang@Sun.COM 				req->rdma_count = 1;
328410253Sxiuyan.wang@Sun.COM 				req->cksum_offset = cksum_offset;
328510253Sxiuyan.wang@Sun.COM 				req->length = htons(seglen);
328610253Sxiuyan.wang@Sun.COM 				req->flags = flags | ((cum_len & 1) *
328710253Sxiuyan.wang@Sun.COM 				    MXGEFW_FLAGS_ALIGN_ODD);
328810253Sxiuyan.wang@Sun.COM 				if (cksum_offset > seglen)
328910253Sxiuyan.wang@Sun.COM 					cksum_offset -= seglen;
329010253Sxiuyan.wang@Sun.COM 				else
329110253Sxiuyan.wang@Sun.COM 					cksum_offset = 0;
329210253Sxiuyan.wang@Sun.COM 				low += seglen;
329310253Sxiuyan.wang@Sun.COM 				len -= seglen;
329410253Sxiuyan.wang@Sun.COM 				cum_len = cum_len_next;
329510253Sxiuyan.wang@Sun.COM 				req++;
329610253Sxiuyan.wang@Sun.COM 				req->flags = 0;
329710253Sxiuyan.wang@Sun.COM 				flags = flags_next;
329810253Sxiuyan.wang@Sun.COM 				count++;
329910253Sxiuyan.wang@Sun.COM 				rdma_count++;
330010253Sxiuyan.wang@Sun.COM 			}
330110253Sxiuyan.wang@Sun.COM 		}
330210253Sxiuyan.wang@Sun.COM 		tx_req++;
330310253Sxiuyan.wang@Sun.COM 	}
330410253Sxiuyan.wang@Sun.COM 	(req-rdma_count)->rdma_count = (uint8_t)rdma_count;
330510253Sxiuyan.wang@Sun.COM 	do {
330610253Sxiuyan.wang@Sun.COM 		req--;
330710253Sxiuyan.wang@Sun.COM 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
330810253Sxiuyan.wang@Sun.COM 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP |
330910253Sxiuyan.wang@Sun.COM 	    MXGEFW_FLAGS_FIRST)));
331010253Sxiuyan.wang@Sun.COM 
331110253Sxiuyan.wang@Sun.COM 	myri10ge_submit_req(tx, req_list, count);
331210253Sxiuyan.wang@Sun.COM done:
331310253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->lock);
331410253Sxiuyan.wang@Sun.COM 	freemsg(mp);
331510253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
331610253Sxiuyan.wang@Sun.COM }
331710253Sxiuyan.wang@Sun.COM 
331810253Sxiuyan.wang@Sun.COM /*
331910253Sxiuyan.wang@Sun.COM  * Try to send the chain of buffers described by the mp.  We must not
332010253Sxiuyan.wang@Sun.COM  * encapsulate more than eth->tx.req - eth->tx.done, or
332110253Sxiuyan.wang@Sun.COM  * MXGEFW_MAX_SEND_DESC, whichever is more.
332210253Sxiuyan.wang@Sun.COM  */
332310253Sxiuyan.wang@Sun.COM 
332410253Sxiuyan.wang@Sun.COM static int
myri10ge_send(struct myri10ge_slice_state * ss,mblk_t * mp,mcp_kreq_ether_send_t * req_list,struct myri10ge_tx_buffer_state * tx_info)332510253Sxiuyan.wang@Sun.COM myri10ge_send(struct myri10ge_slice_state *ss, mblk_t *mp,
332610253Sxiuyan.wang@Sun.COM     mcp_kreq_ether_send_t *req_list, struct myri10ge_tx_buffer_state *tx_info)
332710253Sxiuyan.wang@Sun.COM {
332810253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
332910253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx = &ss->tx;
333010253Sxiuyan.wang@Sun.COM 	mcp_kreq_ether_send_t *req;
333110253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_dma_handle *handles, *dma_handle = NULL;
333210253Sxiuyan.wang@Sun.COM 	mblk_t  *bp;
333310253Sxiuyan.wang@Sun.COM 	ddi_dma_cookie_t cookie;
333410253Sxiuyan.wang@Sun.COM 	int err, rv, count, avail, mblen, try_pullup, i, max_segs, maclen,
333510253Sxiuyan.wang@Sun.COM 	    rdma_count, cum_len, lso_hdr_size;
333610253Sxiuyan.wang@Sun.COM 	uint32_t start, stuff, tx_offload_flags;
333710253Sxiuyan.wang@Sun.COM 	uint32_t seglen, len, mss, boundary, low, high_swapped;
333810253Sxiuyan.wang@Sun.COM 	uint_t ncookies;
333910253Sxiuyan.wang@Sun.COM 	uint16_t pseudo_hdr_offset;
334010253Sxiuyan.wang@Sun.COM 	uint8_t flags, cksum_offset, odd_flag;
334110253Sxiuyan.wang@Sun.COM 	int pkt_size;
334210253Sxiuyan.wang@Sun.COM 	int lso_copy = myri10ge_lso_copy;
334310253Sxiuyan.wang@Sun.COM 	try_pullup = 1;
334410253Sxiuyan.wang@Sun.COM 
334510253Sxiuyan.wang@Sun.COM again:
334610253Sxiuyan.wang@Sun.COM 	/* Setup checksum offloading, if needed */
3347*11878SVenu.Iyer@Sun.COM 	mac_hcksum_get(mp, &start, &stuff, NULL, NULL, &tx_offload_flags);
334810253Sxiuyan.wang@Sun.COM 	myri10ge_lso_info_get(mp, &mss, &tx_offload_flags);
334910253Sxiuyan.wang@Sun.COM 	if (tx_offload_flags & HW_LSO) {
335010253Sxiuyan.wang@Sun.COM 		max_segs = MYRI10GE_MAX_SEND_DESC_TSO;
335110253Sxiuyan.wang@Sun.COM 		if ((tx_offload_flags & HCK_PARTIALCKSUM) == 0) {
335210253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_lsobadflags);
335310253Sxiuyan.wang@Sun.COM 			freemsg(mp);
335410253Sxiuyan.wang@Sun.COM 			return (DDI_SUCCESS);
335510253Sxiuyan.wang@Sun.COM 		}
335610253Sxiuyan.wang@Sun.COM 	} else {
335710253Sxiuyan.wang@Sun.COM 		max_segs = MXGEFW_MAX_SEND_DESC;
335810253Sxiuyan.wang@Sun.COM 		mss = 0;
335910253Sxiuyan.wang@Sun.COM 	}
336010253Sxiuyan.wang@Sun.COM 	req = req_list;
336110253Sxiuyan.wang@Sun.COM 	cksum_offset = 0;
336210253Sxiuyan.wang@Sun.COM 	pseudo_hdr_offset = 0;
336310253Sxiuyan.wang@Sun.COM 
336410253Sxiuyan.wang@Sun.COM 	/* leave an extra slot keep the ring from wrapping */
336510253Sxiuyan.wang@Sun.COM 	avail = tx->mask - (tx->req - tx->done);
336610253Sxiuyan.wang@Sun.COM 
336710253Sxiuyan.wang@Sun.COM 	/*
336810253Sxiuyan.wang@Sun.COM 	 * If we have > MXGEFW_MAX_SEND_DESC, then any over-length
336910253Sxiuyan.wang@Sun.COM 	 * message will need to be pulled up in order to fit.
337010253Sxiuyan.wang@Sun.COM 	 * Otherwise, we are low on transmit descriptors, it is
337110253Sxiuyan.wang@Sun.COM 	 * probably better to stall and try again rather than pullup a
337210253Sxiuyan.wang@Sun.COM 	 * message to fit.
337310253Sxiuyan.wang@Sun.COM 	 */
337410253Sxiuyan.wang@Sun.COM 
337510253Sxiuyan.wang@Sun.COM 	if (avail < max_segs) {
337610253Sxiuyan.wang@Sun.COM 		err = EBUSY;
337710253Sxiuyan.wang@Sun.COM 		atomic_add_32(&tx->stall_early, 1);
337810253Sxiuyan.wang@Sun.COM 		goto stall;
337910253Sxiuyan.wang@Sun.COM 	}
338010253Sxiuyan.wang@Sun.COM 
338110253Sxiuyan.wang@Sun.COM 	/* find out how long the frame is and how many segments it is */
338210253Sxiuyan.wang@Sun.COM 	count = 0;
338310253Sxiuyan.wang@Sun.COM 	odd_flag = 0;
338410253Sxiuyan.wang@Sun.COM 	pkt_size = 0;
338510253Sxiuyan.wang@Sun.COM 	flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST);
338610253Sxiuyan.wang@Sun.COM 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
338710253Sxiuyan.wang@Sun.COM 		dblk_t *dbp;
338810253Sxiuyan.wang@Sun.COM 		mblen = MBLKL(bp);
338910253Sxiuyan.wang@Sun.COM 		if (mblen == 0) {
339010253Sxiuyan.wang@Sun.COM 			/*
339110253Sxiuyan.wang@Sun.COM 			 * we can't simply skip over 0-length mblks
339210253Sxiuyan.wang@Sun.COM 			 * because the hardware can't deal with them,
339310253Sxiuyan.wang@Sun.COM 			 * and we could leak them.
339410253Sxiuyan.wang@Sun.COM 			 */
339510253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_zero_len);
339610253Sxiuyan.wang@Sun.COM 			err = EIO;
339710253Sxiuyan.wang@Sun.COM 			goto pullup;
339810253Sxiuyan.wang@Sun.COM 		}
339910253Sxiuyan.wang@Sun.COM 		/*
340010253Sxiuyan.wang@Sun.COM 		 * There's no advantage to copying most gesballoc
340110253Sxiuyan.wang@Sun.COM 		 * attached blocks, so disable lso copy in that case
340210253Sxiuyan.wang@Sun.COM 		 */
340310253Sxiuyan.wang@Sun.COM 		if (mss && lso_copy == 1 && ((dbp = bp->b_datap) != NULL)) {
340410253Sxiuyan.wang@Sun.COM 			if ((void *)dbp->db_lastfree != myri10ge_db_lastfree) {
340510253Sxiuyan.wang@Sun.COM 				lso_copy = 0;
340610253Sxiuyan.wang@Sun.COM 			}
340710253Sxiuyan.wang@Sun.COM 		}
340810253Sxiuyan.wang@Sun.COM 		pkt_size += mblen;
340910253Sxiuyan.wang@Sun.COM 		count++;
341010253Sxiuyan.wang@Sun.COM 	}
341110253Sxiuyan.wang@Sun.COM 
341210253Sxiuyan.wang@Sun.COM 	/* Try to pull up excessivly long chains */
341310253Sxiuyan.wang@Sun.COM 	if (count >= max_segs) {
341410253Sxiuyan.wang@Sun.COM 		err = myri10ge_pullup(ss, mp);
341510253Sxiuyan.wang@Sun.COM 		if (likely(err == DDI_SUCCESS)) {
341610253Sxiuyan.wang@Sun.COM 			count = 1;
341710253Sxiuyan.wang@Sun.COM 		} else {
341810253Sxiuyan.wang@Sun.COM 			if (count <  MYRI10GE_MAX_SEND_DESC_TSO) {
341910253Sxiuyan.wang@Sun.COM 				/*
342010253Sxiuyan.wang@Sun.COM 				 * just let the h/w send it, it will be
342110253Sxiuyan.wang@Sun.COM 				 * inefficient, but us better than dropping
342210253Sxiuyan.wang@Sun.COM 				 */
342310253Sxiuyan.wang@Sun.COM 				max_segs = MYRI10GE_MAX_SEND_DESC_TSO;
342410253Sxiuyan.wang@Sun.COM 			} else {
342510253Sxiuyan.wang@Sun.COM 				/* drop it */
342610253Sxiuyan.wang@Sun.COM 				MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_err);
342710253Sxiuyan.wang@Sun.COM 				freemsg(mp);
342810253Sxiuyan.wang@Sun.COM 				return (0);
342910253Sxiuyan.wang@Sun.COM 			}
343010253Sxiuyan.wang@Sun.COM 		}
343110253Sxiuyan.wang@Sun.COM 	}
343210253Sxiuyan.wang@Sun.COM 
343310253Sxiuyan.wang@Sun.COM 	cum_len = 0;
343410253Sxiuyan.wang@Sun.COM 	maclen = myri10ge_ether_parse_header(mp);
343510253Sxiuyan.wang@Sun.COM 
343610253Sxiuyan.wang@Sun.COM 	if (tx_offload_flags & HCK_PARTIALCKSUM) {
343710253Sxiuyan.wang@Sun.COM 
343810253Sxiuyan.wang@Sun.COM 		cksum_offset = start + maclen;
343910253Sxiuyan.wang@Sun.COM 		pseudo_hdr_offset = htons(stuff + maclen);
344010253Sxiuyan.wang@Sun.COM 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
344110253Sxiuyan.wang@Sun.COM 		flags |= MXGEFW_FLAGS_CKSUM;
344210253Sxiuyan.wang@Sun.COM 	}
344310253Sxiuyan.wang@Sun.COM 
344410253Sxiuyan.wang@Sun.COM 	lso_hdr_size = 0; /* -Wunitinialized */
344510253Sxiuyan.wang@Sun.COM 	if (mss) { /* LSO */
344610253Sxiuyan.wang@Sun.COM 		/* this removes any CKSUM flag from before */
344710253Sxiuyan.wang@Sun.COM 		flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST);
344810253Sxiuyan.wang@Sun.COM 		/*
344910253Sxiuyan.wang@Sun.COM 		 * parse the headers and set cum_len to a negative
345010253Sxiuyan.wang@Sun.COM 		 * value to reflect the offset of the TCP payload
345110253Sxiuyan.wang@Sun.COM 		 */
345210253Sxiuyan.wang@Sun.COM 		lso_hdr_size =  myri10ge_lso_parse_header(mp, maclen);
345310253Sxiuyan.wang@Sun.COM 		cum_len = -lso_hdr_size;
345410253Sxiuyan.wang@Sun.COM 		if ((mss < mgp->tx_boundary) && lso_copy) {
345510253Sxiuyan.wang@Sun.COM 			err = myri10ge_tx_tso_copy(ss, mp, req_list,
345610253Sxiuyan.wang@Sun.COM 			    lso_hdr_size, pkt_size, mss, cksum_offset);
345710253Sxiuyan.wang@Sun.COM 			return (err);
345810253Sxiuyan.wang@Sun.COM 		}
345910253Sxiuyan.wang@Sun.COM 
346010253Sxiuyan.wang@Sun.COM 		/*
346110253Sxiuyan.wang@Sun.COM 		 * for TSO, pseudo_hdr_offset holds mss.  The firmware
346210253Sxiuyan.wang@Sun.COM 		 * figures out where to put the checksum by parsing
346310253Sxiuyan.wang@Sun.COM 		 * the header.
346410253Sxiuyan.wang@Sun.COM 		 */
346510253Sxiuyan.wang@Sun.COM 
346610253Sxiuyan.wang@Sun.COM 		pseudo_hdr_offset = htons(mss);
346710253Sxiuyan.wang@Sun.COM 	} else if (pkt_size <= MXGEFW_SEND_SMALL_SIZE) {
346810253Sxiuyan.wang@Sun.COM 		flags |= MXGEFW_FLAGS_SMALL;
346910253Sxiuyan.wang@Sun.COM 		if (pkt_size < myri10ge_tx_copylen) {
347010253Sxiuyan.wang@Sun.COM 			req->cksum_offset = cksum_offset;
347110253Sxiuyan.wang@Sun.COM 			req->pseudo_hdr_offset = pseudo_hdr_offset;
347210253Sxiuyan.wang@Sun.COM 			req->flags = flags;
347310253Sxiuyan.wang@Sun.COM 			err = myri10ge_tx_copy(ss, mp, req);
347410253Sxiuyan.wang@Sun.COM 			return (err);
347510253Sxiuyan.wang@Sun.COM 		}
347610253Sxiuyan.wang@Sun.COM 		cum_len = 0;
347710253Sxiuyan.wang@Sun.COM 	}
347810253Sxiuyan.wang@Sun.COM 
347910253Sxiuyan.wang@Sun.COM 	/* pull one DMA handle for each bp from our freelist */
348010253Sxiuyan.wang@Sun.COM 	handles = NULL;
348110253Sxiuyan.wang@Sun.COM 	err = myri10ge_alloc_tx_handles(ss, count, &handles);
348210253Sxiuyan.wang@Sun.COM 	if (err != DDI_SUCCESS) {
348310253Sxiuyan.wang@Sun.COM 		err = DDI_FAILURE;
348410253Sxiuyan.wang@Sun.COM 		goto stall;
348510253Sxiuyan.wang@Sun.COM 	}
348610253Sxiuyan.wang@Sun.COM 	count = 0;
348710253Sxiuyan.wang@Sun.COM 	rdma_count = 0;
348810253Sxiuyan.wang@Sun.COM 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
348910253Sxiuyan.wang@Sun.COM 		mblen = MBLKL(bp);
349010253Sxiuyan.wang@Sun.COM 		dma_handle = handles;
349110253Sxiuyan.wang@Sun.COM 		handles = handles->next;
349210253Sxiuyan.wang@Sun.COM 
349310253Sxiuyan.wang@Sun.COM 		rv = ddi_dma_addr_bind_handle(dma_handle->h, NULL,
349410253Sxiuyan.wang@Sun.COM 		    (caddr_t)bp->b_rptr, mblen,
349510253Sxiuyan.wang@Sun.COM 		    DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
349610253Sxiuyan.wang@Sun.COM 		    &cookie, &ncookies);
349710253Sxiuyan.wang@Sun.COM 		if (unlikely(rv != DDI_DMA_MAPPED)) {
349810253Sxiuyan.wang@Sun.COM 			err = EIO;
349910253Sxiuyan.wang@Sun.COM 			try_pullup = 0;
350010253Sxiuyan.wang@Sun.COM 			dma_handle->next = handles;
350110253Sxiuyan.wang@Sun.COM 			handles = dma_handle;
350210253Sxiuyan.wang@Sun.COM 			goto abort_with_handles;
350310253Sxiuyan.wang@Sun.COM 		}
350410253Sxiuyan.wang@Sun.COM 
350510253Sxiuyan.wang@Sun.COM 		/* reserve the slot */
350610253Sxiuyan.wang@Sun.COM 		tx_info[count].m = bp;
350710253Sxiuyan.wang@Sun.COM 		tx_info[count].handle = dma_handle;
350810253Sxiuyan.wang@Sun.COM 
350910253Sxiuyan.wang@Sun.COM 		for (; ; ) {
351010253Sxiuyan.wang@Sun.COM 			low = MYRI10GE_LOWPART_TO_U32(cookie.dmac_laddress);
351110253Sxiuyan.wang@Sun.COM 			high_swapped =
351210253Sxiuyan.wang@Sun.COM 			    htonl(MYRI10GE_HIGHPART_TO_U32(
351310253Sxiuyan.wang@Sun.COM 			    cookie.dmac_laddress));
351410253Sxiuyan.wang@Sun.COM 			len = (uint32_t)cookie.dmac_size;
351510253Sxiuyan.wang@Sun.COM 			while (len) {
351610253Sxiuyan.wang@Sun.COM 				uint8_t flags_next;
351710253Sxiuyan.wang@Sun.COM 				int cum_len_next;
351810253Sxiuyan.wang@Sun.COM 
351910253Sxiuyan.wang@Sun.COM 				boundary = (low + mgp->tx_boundary) &
352010253Sxiuyan.wang@Sun.COM 				    ~(mgp->tx_boundary - 1);
352110253Sxiuyan.wang@Sun.COM 				seglen = boundary - low;
352210253Sxiuyan.wang@Sun.COM 				if (seglen > len)
352310253Sxiuyan.wang@Sun.COM 					seglen = len;
352410253Sxiuyan.wang@Sun.COM 
352510253Sxiuyan.wang@Sun.COM 				flags_next = flags & ~MXGEFW_FLAGS_FIRST;
352610253Sxiuyan.wang@Sun.COM 				cum_len_next = cum_len + seglen;
352710253Sxiuyan.wang@Sun.COM 				if (mss) {
352810253Sxiuyan.wang@Sun.COM 					(req-rdma_count)->rdma_count =
352910253Sxiuyan.wang@Sun.COM 					    rdma_count + 1;
353010253Sxiuyan.wang@Sun.COM 					if (likely(cum_len >= 0)) {
353110253Sxiuyan.wang@Sun.COM 						/* payload */
353210253Sxiuyan.wang@Sun.COM 						int next_is_first, chop;
353310253Sxiuyan.wang@Sun.COM 
353410253Sxiuyan.wang@Sun.COM 						chop = (cum_len_next > mss);
353510253Sxiuyan.wang@Sun.COM 						cum_len_next =
353610253Sxiuyan.wang@Sun.COM 						    cum_len_next % mss;
353710253Sxiuyan.wang@Sun.COM 						next_is_first =
353810253Sxiuyan.wang@Sun.COM 						    (cum_len_next == 0);
353910253Sxiuyan.wang@Sun.COM 						flags |= chop *
354010253Sxiuyan.wang@Sun.COM 						    MXGEFW_FLAGS_TSO_CHOP;
354110253Sxiuyan.wang@Sun.COM 						flags_next |= next_is_first *
354210253Sxiuyan.wang@Sun.COM 						    MXGEFW_FLAGS_FIRST;
354310253Sxiuyan.wang@Sun.COM 						rdma_count |=
354410253Sxiuyan.wang@Sun.COM 						    -(chop | next_is_first);
354510253Sxiuyan.wang@Sun.COM 						rdma_count +=
354610253Sxiuyan.wang@Sun.COM 						    chop & !next_is_first;
354710253Sxiuyan.wang@Sun.COM 					} else if (likely(cum_len_next >= 0)) {
354810253Sxiuyan.wang@Sun.COM 						/* header ends */
354910253Sxiuyan.wang@Sun.COM 						int small;
355010253Sxiuyan.wang@Sun.COM 
355110253Sxiuyan.wang@Sun.COM 						rdma_count = -1;
355210253Sxiuyan.wang@Sun.COM 						cum_len_next = 0;
355310253Sxiuyan.wang@Sun.COM 						seglen = -cum_len;
355410253Sxiuyan.wang@Sun.COM 						small = (mss <=
355510253Sxiuyan.wang@Sun.COM 						    MXGEFW_SEND_SMALL_SIZE);
355610253Sxiuyan.wang@Sun.COM 						flags_next =
355710253Sxiuyan.wang@Sun.COM 						    MXGEFW_FLAGS_TSO_PLD
355810253Sxiuyan.wang@Sun.COM 						    | MXGEFW_FLAGS_FIRST
355910253Sxiuyan.wang@Sun.COM 						    | (small *
356010253Sxiuyan.wang@Sun.COM 						    MXGEFW_FLAGS_SMALL);
356110253Sxiuyan.wang@Sun.COM 					}
356210253Sxiuyan.wang@Sun.COM 				}
356310253Sxiuyan.wang@Sun.COM 				req->addr_high = high_swapped;
356410253Sxiuyan.wang@Sun.COM 				req->addr_low = htonl(low);
356510253Sxiuyan.wang@Sun.COM 				req->pseudo_hdr_offset = pseudo_hdr_offset;
356610253Sxiuyan.wang@Sun.COM 				req->pad = 0; /* complete solid 16-byte block */
356710253Sxiuyan.wang@Sun.COM 				req->rdma_count = 1;
356810253Sxiuyan.wang@Sun.COM 				req->cksum_offset = cksum_offset;
356910253Sxiuyan.wang@Sun.COM 				req->length = htons(seglen);
357010253Sxiuyan.wang@Sun.COM 				req->flags = flags | ((cum_len & 1) * odd_flag);
357110253Sxiuyan.wang@Sun.COM 				if (cksum_offset > seglen)
357210253Sxiuyan.wang@Sun.COM 					cksum_offset -= seglen;
357310253Sxiuyan.wang@Sun.COM 				else
357410253Sxiuyan.wang@Sun.COM 					cksum_offset = 0;
357510253Sxiuyan.wang@Sun.COM 				low += seglen;
357610253Sxiuyan.wang@Sun.COM 				len -= seglen;
357710253Sxiuyan.wang@Sun.COM 				cum_len = cum_len_next;
357810253Sxiuyan.wang@Sun.COM 				count++;
357910253Sxiuyan.wang@Sun.COM 				rdma_count++;
358010253Sxiuyan.wang@Sun.COM 				/*  make sure all the segments will fit */
358110253Sxiuyan.wang@Sun.COM 				if (unlikely(count >= max_segs)) {
358210253Sxiuyan.wang@Sun.COM 					MYRI10GE_ATOMIC_SLICE_STAT_INC(
358310253Sxiuyan.wang@Sun.COM 					    xmit_lowbuf);
358410253Sxiuyan.wang@Sun.COM 					/* may try a pullup */
358510253Sxiuyan.wang@Sun.COM 					err = EBUSY;
358610253Sxiuyan.wang@Sun.COM 					if (try_pullup)
358710253Sxiuyan.wang@Sun.COM 						try_pullup = 2;
358810253Sxiuyan.wang@Sun.COM 					goto abort_with_handles;
358910253Sxiuyan.wang@Sun.COM 				}
359010253Sxiuyan.wang@Sun.COM 				req++;
359110253Sxiuyan.wang@Sun.COM 				req->flags = 0;
359210253Sxiuyan.wang@Sun.COM 				flags = flags_next;
359310253Sxiuyan.wang@Sun.COM 				tx_info[count].m = 0;
359410253Sxiuyan.wang@Sun.COM 			}
359510253Sxiuyan.wang@Sun.COM 			ncookies--;
359610253Sxiuyan.wang@Sun.COM 			if (ncookies == 0)
359710253Sxiuyan.wang@Sun.COM 				break;
359810253Sxiuyan.wang@Sun.COM 			ddi_dma_nextcookie(dma_handle->h, &cookie);
359910253Sxiuyan.wang@Sun.COM 		}
360010253Sxiuyan.wang@Sun.COM 	}
360110253Sxiuyan.wang@Sun.COM 	(req-rdma_count)->rdma_count = (uint8_t)rdma_count;
360210253Sxiuyan.wang@Sun.COM 
360310253Sxiuyan.wang@Sun.COM 	if (mss) {
360410253Sxiuyan.wang@Sun.COM 		do {
360510253Sxiuyan.wang@Sun.COM 			req--;
360610253Sxiuyan.wang@Sun.COM 			req->flags |= MXGEFW_FLAGS_TSO_LAST;
360710253Sxiuyan.wang@Sun.COM 		} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP |
360810253Sxiuyan.wang@Sun.COM 		    MXGEFW_FLAGS_FIRST)));
360910253Sxiuyan.wang@Sun.COM 	}
361010253Sxiuyan.wang@Sun.COM 
361110253Sxiuyan.wang@Sun.COM 	/* calculate tx stats */
361210253Sxiuyan.wang@Sun.COM 	if (mss) {
361310253Sxiuyan.wang@Sun.COM 		uint16_t opackets;
361410253Sxiuyan.wang@Sun.COM 		int payload;
361510253Sxiuyan.wang@Sun.COM 
361610253Sxiuyan.wang@Sun.COM 		payload = pkt_size - lso_hdr_size;
361710253Sxiuyan.wang@Sun.COM 		opackets = (payload / mss) + ((payload % mss) != 0);
361810253Sxiuyan.wang@Sun.COM 		tx_info[0].stat.un.all = 0;
361910253Sxiuyan.wang@Sun.COM 		tx_info[0].ostat.opackets = opackets;
362010253Sxiuyan.wang@Sun.COM 		tx_info[0].ostat.obytes = (opackets - 1) * lso_hdr_size
362110253Sxiuyan.wang@Sun.COM 		    + pkt_size;
362210253Sxiuyan.wang@Sun.COM 	} else {
362310253Sxiuyan.wang@Sun.COM 		myri10ge_tx_stat(&tx_info[0].stat,
362410253Sxiuyan.wang@Sun.COM 		    (struct ether_header *)(void *)mp->b_rptr, 1, pkt_size);
362510253Sxiuyan.wang@Sun.COM 	}
362610253Sxiuyan.wang@Sun.COM 	mutex_enter(&tx->lock);
362710253Sxiuyan.wang@Sun.COM 
362810253Sxiuyan.wang@Sun.COM 	/* check to see if the slots are really there */
362910253Sxiuyan.wang@Sun.COM 	avail = tx->mask - (tx->req - tx->done);
363010253Sxiuyan.wang@Sun.COM 	if (unlikely(avail <= count)) {
363110253Sxiuyan.wang@Sun.COM 		mutex_exit(&tx->lock);
363210253Sxiuyan.wang@Sun.COM 		err = 0;
363310253Sxiuyan.wang@Sun.COM 		goto late_stall;
363410253Sxiuyan.wang@Sun.COM 	}
363510253Sxiuyan.wang@Sun.COM 
363610253Sxiuyan.wang@Sun.COM 	myri10ge_send_locked(tx, req_list, tx_info, count);
363710253Sxiuyan.wang@Sun.COM 	mutex_exit(&tx->lock);
363810253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
363910253Sxiuyan.wang@Sun.COM 
364010253Sxiuyan.wang@Sun.COM late_stall:
364110253Sxiuyan.wang@Sun.COM 	try_pullup = 0;
364210253Sxiuyan.wang@Sun.COM 	atomic_add_32(&tx->stall_late, 1);
364310253Sxiuyan.wang@Sun.COM 
364410253Sxiuyan.wang@Sun.COM abort_with_handles:
364510253Sxiuyan.wang@Sun.COM 	/* unbind and free handles from previous mblks */
364610253Sxiuyan.wang@Sun.COM 	for (i = 0; i < count; i++) {
364710253Sxiuyan.wang@Sun.COM 		bp = tx_info[i].m;
364810253Sxiuyan.wang@Sun.COM 		tx_info[i].m = 0;
364910253Sxiuyan.wang@Sun.COM 		if (bp) {
365010253Sxiuyan.wang@Sun.COM 			dma_handle = tx_info[i].handle;
365110253Sxiuyan.wang@Sun.COM 			(void) ddi_dma_unbind_handle(dma_handle->h);
365210253Sxiuyan.wang@Sun.COM 			dma_handle->next = handles;
365310253Sxiuyan.wang@Sun.COM 			handles = dma_handle;
365410253Sxiuyan.wang@Sun.COM 			tx_info[i].handle = NULL;
365510253Sxiuyan.wang@Sun.COM 			tx_info[i].m = NULL;
365610253Sxiuyan.wang@Sun.COM 		}
365710253Sxiuyan.wang@Sun.COM 	}
365810253Sxiuyan.wang@Sun.COM 	myri10ge_free_tx_handle_slist(tx, handles);
365910253Sxiuyan.wang@Sun.COM pullup:
366010253Sxiuyan.wang@Sun.COM 	if (try_pullup) {
366110253Sxiuyan.wang@Sun.COM 		err = myri10ge_pullup(ss, mp);
366210253Sxiuyan.wang@Sun.COM 		if (err != DDI_SUCCESS && try_pullup == 2) {
366310253Sxiuyan.wang@Sun.COM 			/* drop */
366410253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_err);
366510253Sxiuyan.wang@Sun.COM 			freemsg(mp);
366610253Sxiuyan.wang@Sun.COM 			return (0);
366710253Sxiuyan.wang@Sun.COM 		}
366810253Sxiuyan.wang@Sun.COM 		try_pullup = 0;
366910253Sxiuyan.wang@Sun.COM 		goto again;
367010253Sxiuyan.wang@Sun.COM 	}
367110253Sxiuyan.wang@Sun.COM 
367210253Sxiuyan.wang@Sun.COM stall:
367310253Sxiuyan.wang@Sun.COM 	if (err != 0) {
367410253Sxiuyan.wang@Sun.COM 		if (err == EBUSY) {
367510253Sxiuyan.wang@Sun.COM 			atomic_add_32(&tx->stall, 1);
367610253Sxiuyan.wang@Sun.COM 		} else {
367710253Sxiuyan.wang@Sun.COM 			MYRI10GE_ATOMIC_SLICE_STAT_INC(xmit_err);
367810253Sxiuyan.wang@Sun.COM 		}
367910253Sxiuyan.wang@Sun.COM 	}
368010253Sxiuyan.wang@Sun.COM 	return (err);
368110253Sxiuyan.wang@Sun.COM }
368210253Sxiuyan.wang@Sun.COM 
368310253Sxiuyan.wang@Sun.COM static mblk_t *
myri10ge_send_wrapper(void * arg,mblk_t * mp)368410253Sxiuyan.wang@Sun.COM myri10ge_send_wrapper(void *arg, mblk_t *mp)
368510253Sxiuyan.wang@Sun.COM {
368610253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss = arg;
368710253Sxiuyan.wang@Sun.COM 	int err = 0;
368810253Sxiuyan.wang@Sun.COM 	mcp_kreq_ether_send_t *req_list;
368910253Sxiuyan.wang@Sun.COM #if defined(__i386)
369010253Sxiuyan.wang@Sun.COM 	/*
369110253Sxiuyan.wang@Sun.COM 	 * We need about 2.5KB of scratch space to handle transmits.
369210253Sxiuyan.wang@Sun.COM 	 * i86pc has only 8KB of kernel stack space, so we malloc the
369310253Sxiuyan.wang@Sun.COM 	 * scratch space there rather than keeping it on the stack.
369410253Sxiuyan.wang@Sun.COM 	 */
369510253Sxiuyan.wang@Sun.COM 	size_t req_size, tx_info_size;
369610253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_buffer_state *tx_info;
369710253Sxiuyan.wang@Sun.COM 	caddr_t req_bytes;
369810253Sxiuyan.wang@Sun.COM 
369910253Sxiuyan.wang@Sun.COM 	req_size = sizeof (*req_list) * (MYRI10GE_MAX_SEND_DESC_TSO + 4)
370010253Sxiuyan.wang@Sun.COM 	    + 8;
370110253Sxiuyan.wang@Sun.COM 	req_bytes = kmem_alloc(req_size, KM_SLEEP);
370210253Sxiuyan.wang@Sun.COM 	tx_info_size = sizeof (*tx_info) * (MYRI10GE_MAX_SEND_DESC_TSO + 1);
370310253Sxiuyan.wang@Sun.COM 	tx_info = kmem_alloc(tx_info_size, KM_SLEEP);
370410253Sxiuyan.wang@Sun.COM #else
370510253Sxiuyan.wang@Sun.COM 	char req_bytes[sizeof (*req_list) * (MYRI10GE_MAX_SEND_DESC_TSO + 4)
370610253Sxiuyan.wang@Sun.COM 	    + 8];
370710253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_buffer_state tx_info[MYRI10GE_MAX_SEND_DESC_TSO + 1];
370810253Sxiuyan.wang@Sun.COM #endif
370910253Sxiuyan.wang@Sun.COM 
371010253Sxiuyan.wang@Sun.COM 	/* ensure req_list entries are aligned to 8 bytes */
371110253Sxiuyan.wang@Sun.COM 	req_list = (struct mcp_kreq_ether_send *)
371210253Sxiuyan.wang@Sun.COM 	    (((unsigned long)req_bytes + 7UL) & ~7UL);
371310253Sxiuyan.wang@Sun.COM 
371410253Sxiuyan.wang@Sun.COM 	err = myri10ge_send(ss, mp, req_list, tx_info);
371510253Sxiuyan.wang@Sun.COM 
371610253Sxiuyan.wang@Sun.COM #if defined(__i386)
371710253Sxiuyan.wang@Sun.COM 	kmem_free(tx_info, tx_info_size);
371810253Sxiuyan.wang@Sun.COM 	kmem_free(req_bytes, req_size);
371910253Sxiuyan.wang@Sun.COM #endif
372010253Sxiuyan.wang@Sun.COM 	if (err)
372110253Sxiuyan.wang@Sun.COM 		return (mp);
372210253Sxiuyan.wang@Sun.COM 	else
372310253Sxiuyan.wang@Sun.COM 		return (NULL);
372410253Sxiuyan.wang@Sun.COM }
372510253Sxiuyan.wang@Sun.COM 
372610253Sxiuyan.wang@Sun.COM static int
myri10ge_addmac(void * arg,const uint8_t * mac_addr)372710253Sxiuyan.wang@Sun.COM myri10ge_addmac(void *arg, const uint8_t *mac_addr)
372810253Sxiuyan.wang@Sun.COM {
372910253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
373010253Sxiuyan.wang@Sun.COM 	int err;
373110253Sxiuyan.wang@Sun.COM 
373210253Sxiuyan.wang@Sun.COM 	if (mac_addr == NULL)
373310253Sxiuyan.wang@Sun.COM 		return (EINVAL);
373410253Sxiuyan.wang@Sun.COM 
373510253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
373610253Sxiuyan.wang@Sun.COM 	if (mgp->macaddr_cnt) {
373710253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
373810253Sxiuyan.wang@Sun.COM 		return (ENOSPC);
373910253Sxiuyan.wang@Sun.COM 	}
374010253Sxiuyan.wang@Sun.COM 	err = myri10ge_m_unicst(mgp, mac_addr);
374110253Sxiuyan.wang@Sun.COM 	if (!err)
374210253Sxiuyan.wang@Sun.COM 		mgp->macaddr_cnt++;
374310253Sxiuyan.wang@Sun.COM 
374410253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
374510253Sxiuyan.wang@Sun.COM 	if (err)
374610253Sxiuyan.wang@Sun.COM 		return (err);
374710253Sxiuyan.wang@Sun.COM 
374810253Sxiuyan.wang@Sun.COM 	bcopy(mac_addr, mgp->mac_addr, sizeof (mgp->mac_addr));
374910253Sxiuyan.wang@Sun.COM 	return (0);
375010253Sxiuyan.wang@Sun.COM }
375110253Sxiuyan.wang@Sun.COM 
375210253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
375310253Sxiuyan.wang@Sun.COM static int
myri10ge_remmac(void * arg,const uint8_t * mac_addr)375410253Sxiuyan.wang@Sun.COM myri10ge_remmac(void *arg, const uint8_t *mac_addr)
375510253Sxiuyan.wang@Sun.COM {
375610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
375710253Sxiuyan.wang@Sun.COM 
375810253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
375910253Sxiuyan.wang@Sun.COM 	mgp->macaddr_cnt--;
376010253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
376110253Sxiuyan.wang@Sun.COM 
376210253Sxiuyan.wang@Sun.COM 	return (0);
376310253Sxiuyan.wang@Sun.COM }
376410253Sxiuyan.wang@Sun.COM 
376510253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
376610253Sxiuyan.wang@Sun.COM static void
myri10ge_fill_group(void * arg,mac_ring_type_t rtype,const int index,mac_group_info_t * infop,mac_group_handle_t gh)376710253Sxiuyan.wang@Sun.COM myri10ge_fill_group(void *arg, mac_ring_type_t rtype, const int index,
376810253Sxiuyan.wang@Sun.COM     mac_group_info_t *infop, mac_group_handle_t gh)
376910253Sxiuyan.wang@Sun.COM {
377010253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
377110253Sxiuyan.wang@Sun.COM 
377210253Sxiuyan.wang@Sun.COM 	if (rtype != MAC_RING_TYPE_RX)
377310253Sxiuyan.wang@Sun.COM 		return;
377410253Sxiuyan.wang@Sun.COM 
377510253Sxiuyan.wang@Sun.COM 	infop->mgi_driver = (mac_group_driver_t)mgp;
377610253Sxiuyan.wang@Sun.COM 	infop->mgi_start = NULL;
377710253Sxiuyan.wang@Sun.COM 	infop->mgi_stop = NULL;
377810253Sxiuyan.wang@Sun.COM 	infop->mgi_addmac = myri10ge_addmac;
377910253Sxiuyan.wang@Sun.COM 	infop->mgi_remmac = myri10ge_remmac;
378010253Sxiuyan.wang@Sun.COM 	infop->mgi_count = mgp->num_slices;
378110253Sxiuyan.wang@Sun.COM }
378210253Sxiuyan.wang@Sun.COM 
378310253Sxiuyan.wang@Sun.COM static int
myri10ge_ring_start(mac_ring_driver_t rh,uint64_t mr_gen_num)378410253Sxiuyan.wang@Sun.COM myri10ge_ring_start(mac_ring_driver_t rh, uint64_t mr_gen_num)
378510253Sxiuyan.wang@Sun.COM {
378610253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
378710253Sxiuyan.wang@Sun.COM 
378810253Sxiuyan.wang@Sun.COM 	ss = (struct myri10ge_slice_state *)rh;
378910253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->rx_lock);
379010253Sxiuyan.wang@Sun.COM 	ss->rx_gen_num = mr_gen_num;
379110253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->rx_lock);
379210253Sxiuyan.wang@Sun.COM 	return (0);
379310253Sxiuyan.wang@Sun.COM }
379410253Sxiuyan.wang@Sun.COM 
3795*11878SVenu.Iyer@Sun.COM /*
3796*11878SVenu.Iyer@Sun.COM  * Retrieve a value for one of the statistics for a particular rx ring
3797*11878SVenu.Iyer@Sun.COM  */
3798*11878SVenu.Iyer@Sun.COM int
myri10ge_rx_ring_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)3799*11878SVenu.Iyer@Sun.COM myri10ge_rx_ring_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val)
3800*11878SVenu.Iyer@Sun.COM {
3801*11878SVenu.Iyer@Sun.COM 	struct myri10ge_slice_state *ss;
3802*11878SVenu.Iyer@Sun.COM 
3803*11878SVenu.Iyer@Sun.COM 	ss = (struct myri10ge_slice_state *)rh;
3804*11878SVenu.Iyer@Sun.COM 	switch (stat) {
3805*11878SVenu.Iyer@Sun.COM 	case MAC_STAT_RBYTES:
3806*11878SVenu.Iyer@Sun.COM 		*val = ss->rx_stats.ibytes;
3807*11878SVenu.Iyer@Sun.COM 		break;
3808*11878SVenu.Iyer@Sun.COM 
3809*11878SVenu.Iyer@Sun.COM 	case MAC_STAT_IPACKETS:
3810*11878SVenu.Iyer@Sun.COM 		*val = ss->rx_stats.ipackets;
3811*11878SVenu.Iyer@Sun.COM 		break;
3812*11878SVenu.Iyer@Sun.COM 
3813*11878SVenu.Iyer@Sun.COM 	default:
3814*11878SVenu.Iyer@Sun.COM 		*val = 0;
3815*11878SVenu.Iyer@Sun.COM 		return (ENOTSUP);
3816*11878SVenu.Iyer@Sun.COM 	}
3817*11878SVenu.Iyer@Sun.COM 
3818*11878SVenu.Iyer@Sun.COM 	return (0);
3819*11878SVenu.Iyer@Sun.COM }
3820*11878SVenu.Iyer@Sun.COM 
3821*11878SVenu.Iyer@Sun.COM /*
3822*11878SVenu.Iyer@Sun.COM  * Retrieve a value for one of the statistics for a particular tx ring
3823*11878SVenu.Iyer@Sun.COM  */
3824*11878SVenu.Iyer@Sun.COM int
myri10ge_tx_ring_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)3825*11878SVenu.Iyer@Sun.COM myri10ge_tx_ring_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val)
3826*11878SVenu.Iyer@Sun.COM {
3827*11878SVenu.Iyer@Sun.COM 	struct myri10ge_slice_state *ss;
3828*11878SVenu.Iyer@Sun.COM 
3829*11878SVenu.Iyer@Sun.COM 	ss = (struct myri10ge_slice_state *)rh;
3830*11878SVenu.Iyer@Sun.COM 	switch (stat) {
3831*11878SVenu.Iyer@Sun.COM 	case MAC_STAT_OBYTES:
3832*11878SVenu.Iyer@Sun.COM 		*val = ss->tx.stats.obytes;
3833*11878SVenu.Iyer@Sun.COM 		break;
3834*11878SVenu.Iyer@Sun.COM 
3835*11878SVenu.Iyer@Sun.COM 	case MAC_STAT_OPACKETS:
3836*11878SVenu.Iyer@Sun.COM 		*val = ss->tx.stats.opackets;
3837*11878SVenu.Iyer@Sun.COM 		break;
3838*11878SVenu.Iyer@Sun.COM 
3839*11878SVenu.Iyer@Sun.COM 	default:
3840*11878SVenu.Iyer@Sun.COM 		*val = 0;
3841*11878SVenu.Iyer@Sun.COM 		return (ENOTSUP);
3842*11878SVenu.Iyer@Sun.COM 	}
3843*11878SVenu.Iyer@Sun.COM 
3844*11878SVenu.Iyer@Sun.COM 	return (0);
3845*11878SVenu.Iyer@Sun.COM }
3846*11878SVenu.Iyer@Sun.COM 
384710253Sxiuyan.wang@Sun.COM static int
myri10ge_rx_ring_intr_disable(mac_intr_handle_t intrh)384810253Sxiuyan.wang@Sun.COM myri10ge_rx_ring_intr_disable(mac_intr_handle_t intrh)
384910253Sxiuyan.wang@Sun.COM {
385010253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
385110253Sxiuyan.wang@Sun.COM 
385210253Sxiuyan.wang@Sun.COM 	ss = (struct myri10ge_slice_state *)intrh;
385310253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->poll_lock);
385410253Sxiuyan.wang@Sun.COM 	ss->rx_polling = B_TRUE;
385510253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->poll_lock);
385610253Sxiuyan.wang@Sun.COM 	return (0);
385710253Sxiuyan.wang@Sun.COM }
385810253Sxiuyan.wang@Sun.COM 
385910253Sxiuyan.wang@Sun.COM static int
myri10ge_rx_ring_intr_enable(mac_intr_handle_t intrh)386010253Sxiuyan.wang@Sun.COM myri10ge_rx_ring_intr_enable(mac_intr_handle_t intrh)
386110253Sxiuyan.wang@Sun.COM {
386210253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
386310253Sxiuyan.wang@Sun.COM 
386410253Sxiuyan.wang@Sun.COM 	ss = (struct myri10ge_slice_state *)intrh;
386510253Sxiuyan.wang@Sun.COM 	mutex_enter(&ss->poll_lock);
386610253Sxiuyan.wang@Sun.COM 	ss->rx_polling = B_FALSE;
386710253Sxiuyan.wang@Sun.COM 	if (ss->rx_token) {
386810253Sxiuyan.wang@Sun.COM 		*ss->irq_claim = BE_32(3);
386910253Sxiuyan.wang@Sun.COM 		ss->rx_token = 0;
387010253Sxiuyan.wang@Sun.COM 	}
387110253Sxiuyan.wang@Sun.COM 	mutex_exit(&ss->poll_lock);
387210253Sxiuyan.wang@Sun.COM 	return (0);
387310253Sxiuyan.wang@Sun.COM }
387410253Sxiuyan.wang@Sun.COM 
387510253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
387610253Sxiuyan.wang@Sun.COM static void
myri10ge_fill_ring(void * arg,mac_ring_type_t rtype,const int rg_index,const int ring_index,mac_ring_info_t * infop,mac_ring_handle_t rh)387710253Sxiuyan.wang@Sun.COM myri10ge_fill_ring(void *arg, mac_ring_type_t rtype, const int rg_index,
387810253Sxiuyan.wang@Sun.COM     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
387910253Sxiuyan.wang@Sun.COM {
388010253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
388110253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
388210253Sxiuyan.wang@Sun.COM 	mac_intr_t *mintr = &infop->mri_intr;
388310253Sxiuyan.wang@Sun.COM 
388410253Sxiuyan.wang@Sun.COM 	ASSERT((unsigned int)ring_index < mgp->num_slices);
388510253Sxiuyan.wang@Sun.COM 
388610253Sxiuyan.wang@Sun.COM 	ss = &mgp->ss[ring_index];
388710253Sxiuyan.wang@Sun.COM 	switch (rtype) {
388810253Sxiuyan.wang@Sun.COM 	case MAC_RING_TYPE_RX:
388910253Sxiuyan.wang@Sun.COM 		ss->rx_rh = rh;
389010253Sxiuyan.wang@Sun.COM 		infop->mri_driver = (mac_ring_driver_t)ss;
389110253Sxiuyan.wang@Sun.COM 		infop->mri_start = myri10ge_ring_start;
389210253Sxiuyan.wang@Sun.COM 		infop->mri_stop = NULL;
389310253Sxiuyan.wang@Sun.COM 		infop->mri_poll = myri10ge_poll_rx;
3894*11878SVenu.Iyer@Sun.COM 		infop->mri_stat = myri10ge_rx_ring_stat;
389510253Sxiuyan.wang@Sun.COM 		mintr->mi_handle = (mac_intr_handle_t)ss;
389610253Sxiuyan.wang@Sun.COM 		mintr->mi_enable = myri10ge_rx_ring_intr_enable;
389710253Sxiuyan.wang@Sun.COM 		mintr->mi_disable = myri10ge_rx_ring_intr_disable;
389810253Sxiuyan.wang@Sun.COM 		break;
389910253Sxiuyan.wang@Sun.COM 	case MAC_RING_TYPE_TX:
390010253Sxiuyan.wang@Sun.COM 		ss->tx.rh = rh;
390110253Sxiuyan.wang@Sun.COM 		infop->mri_driver = (mac_ring_driver_t)ss;
390210253Sxiuyan.wang@Sun.COM 		infop->mri_start = NULL;
390310253Sxiuyan.wang@Sun.COM 		infop->mri_stop = NULL;
390410253Sxiuyan.wang@Sun.COM 		infop->mri_tx = myri10ge_send_wrapper;
3905*11878SVenu.Iyer@Sun.COM 		infop->mri_stat = myri10ge_tx_ring_stat;
390610253Sxiuyan.wang@Sun.COM 		break;
390710253Sxiuyan.wang@Sun.COM 	default:
390810253Sxiuyan.wang@Sun.COM 		break;
390910253Sxiuyan.wang@Sun.COM 	}
391010253Sxiuyan.wang@Sun.COM }
391110253Sxiuyan.wang@Sun.COM 
391210253Sxiuyan.wang@Sun.COM static void
myri10ge_nic_stat_destroy(struct myri10ge_priv * mgp)391310253Sxiuyan.wang@Sun.COM myri10ge_nic_stat_destroy(struct myri10ge_priv *mgp)
391410253Sxiuyan.wang@Sun.COM {
391510253Sxiuyan.wang@Sun.COM 	if (mgp->ksp_stat == NULL)
391610253Sxiuyan.wang@Sun.COM 		return;
391710253Sxiuyan.wang@Sun.COM 
391810253Sxiuyan.wang@Sun.COM 	kstat_delete(mgp->ksp_stat);
391910253Sxiuyan.wang@Sun.COM 	mgp->ksp_stat = NULL;
392010253Sxiuyan.wang@Sun.COM }
392110253Sxiuyan.wang@Sun.COM 
392210253Sxiuyan.wang@Sun.COM static void
myri10ge_slice_stat_destroy(struct myri10ge_slice_state * ss)392310253Sxiuyan.wang@Sun.COM myri10ge_slice_stat_destroy(struct myri10ge_slice_state *ss)
392410253Sxiuyan.wang@Sun.COM {
392510253Sxiuyan.wang@Sun.COM 	if (ss->ksp_stat == NULL)
392610253Sxiuyan.wang@Sun.COM 		return;
392710253Sxiuyan.wang@Sun.COM 
392810253Sxiuyan.wang@Sun.COM 	kstat_delete(ss->ksp_stat);
392910253Sxiuyan.wang@Sun.COM 	ss->ksp_stat = NULL;
393010253Sxiuyan.wang@Sun.COM }
393110253Sxiuyan.wang@Sun.COM 
393210253Sxiuyan.wang@Sun.COM static void
myri10ge_info_destroy(struct myri10ge_priv * mgp)393310253Sxiuyan.wang@Sun.COM myri10ge_info_destroy(struct myri10ge_priv *mgp)
393410253Sxiuyan.wang@Sun.COM {
393510253Sxiuyan.wang@Sun.COM 	if (mgp->ksp_info == NULL)
393610253Sxiuyan.wang@Sun.COM 		return;
393710253Sxiuyan.wang@Sun.COM 
393810253Sxiuyan.wang@Sun.COM 	kstat_delete(mgp->ksp_info);
393910253Sxiuyan.wang@Sun.COM 	mgp->ksp_info = NULL;
394010253Sxiuyan.wang@Sun.COM }
394110253Sxiuyan.wang@Sun.COM 
394210253Sxiuyan.wang@Sun.COM static int
myri10ge_nic_stat_kstat_update(kstat_t * ksp,int rw)394310253Sxiuyan.wang@Sun.COM myri10ge_nic_stat_kstat_update(kstat_t *ksp, int rw)
394410253Sxiuyan.wang@Sun.COM {
394510253Sxiuyan.wang@Sun.COM 	struct myri10ge_nic_stat *ethstat;
394610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
394710253Sxiuyan.wang@Sun.COM 	mcp_irq_data_t *fw_stats;
394810253Sxiuyan.wang@Sun.COM 
394910253Sxiuyan.wang@Sun.COM 
395010253Sxiuyan.wang@Sun.COM 	if (rw == KSTAT_WRITE)
395110253Sxiuyan.wang@Sun.COM 		return (EACCES);
395210253Sxiuyan.wang@Sun.COM 
395310253Sxiuyan.wang@Sun.COM 	ethstat = (struct myri10ge_nic_stat *)ksp->ks_data;
395410253Sxiuyan.wang@Sun.COM 	mgp = (struct myri10ge_priv *)ksp->ks_private;
395510253Sxiuyan.wang@Sun.COM 	fw_stats = mgp->ss[0].fw_stats;
395610253Sxiuyan.wang@Sun.COM 
395710253Sxiuyan.wang@Sun.COM 	ethstat->dma_read_bw_MBs.value.ul = mgp->read_dma;
395810253Sxiuyan.wang@Sun.COM 	ethstat->dma_write_bw_MBs.value.ul = mgp->write_dma;
395910253Sxiuyan.wang@Sun.COM 	ethstat->dma_read_write_bw_MBs.value.ul = mgp->read_write_dma;
396010253Sxiuyan.wang@Sun.COM 	if (myri10ge_tx_dma_attr.dma_attr_flags & DDI_DMA_FORCE_PHYSICAL)
396110253Sxiuyan.wang@Sun.COM 		ethstat->dma_force_physical.value.ul = 1;
396210253Sxiuyan.wang@Sun.COM 	else
396310253Sxiuyan.wang@Sun.COM 		ethstat->dma_force_physical.value.ul = 0;
396410253Sxiuyan.wang@Sun.COM 	ethstat->lanes.value.ul = mgp->pcie_link_width;
396510253Sxiuyan.wang@Sun.COM 	ethstat->dropped_bad_crc32.value.ul =
396610253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_bad_crc32);
396710253Sxiuyan.wang@Sun.COM 	ethstat->dropped_bad_phy.value.ul =
396810253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_bad_phy);
396910253Sxiuyan.wang@Sun.COM 	ethstat->dropped_link_error_or_filtered.value.ul =
397010253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_link_error_or_filtered);
397110253Sxiuyan.wang@Sun.COM 	ethstat->dropped_link_overflow.value.ul =
397210253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_link_overflow);
397310253Sxiuyan.wang@Sun.COM 	ethstat->dropped_multicast_filtered.value.ul =
397410253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_multicast_filtered);
397510253Sxiuyan.wang@Sun.COM 	ethstat->dropped_no_big_buffer.value.ul =
397610253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_no_big_buffer);
397710253Sxiuyan.wang@Sun.COM 	ethstat->dropped_no_small_buffer.value.ul =
397810253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_no_small_buffer);
397910253Sxiuyan.wang@Sun.COM 	ethstat->dropped_overrun.value.ul =
398010253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_overrun);
398110253Sxiuyan.wang@Sun.COM 	ethstat->dropped_pause.value.ul =
398210253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_pause);
398310253Sxiuyan.wang@Sun.COM 	ethstat->dropped_runt.value.ul =
398410253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_runt);
398510253Sxiuyan.wang@Sun.COM 	ethstat->link_up.value.ul =
398610253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->link_up);
398710253Sxiuyan.wang@Sun.COM 	ethstat->dropped_unicast_filtered.value.ul =
398810253Sxiuyan.wang@Sun.COM 	    ntohl(fw_stats->dropped_unicast_filtered);
398910253Sxiuyan.wang@Sun.COM 	return (0);
399010253Sxiuyan.wang@Sun.COM }
399110253Sxiuyan.wang@Sun.COM 
399210253Sxiuyan.wang@Sun.COM static int
myri10ge_slice_stat_kstat_update(kstat_t * ksp,int rw)399310253Sxiuyan.wang@Sun.COM myri10ge_slice_stat_kstat_update(kstat_t *ksp, int rw)
399410253Sxiuyan.wang@Sun.COM {
399510253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_stat *ethstat;
399610253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
399710253Sxiuyan.wang@Sun.COM 
399810253Sxiuyan.wang@Sun.COM 	if (rw == KSTAT_WRITE)
399910253Sxiuyan.wang@Sun.COM 		return (EACCES);
400010253Sxiuyan.wang@Sun.COM 
400110253Sxiuyan.wang@Sun.COM 	ethstat = (struct myri10ge_slice_stat *)ksp->ks_data;
400210253Sxiuyan.wang@Sun.COM 	ss = (struct myri10ge_slice_state *)ksp->ks_private;
400310253Sxiuyan.wang@Sun.COM 
400410253Sxiuyan.wang@Sun.COM 	ethstat->rx_big.value.ul = ss->j_rx_cnt;
400510253Sxiuyan.wang@Sun.COM 	ethstat->rx_bigbuf_firmware.value.ul = ss->rx_big.cnt - ss->j_rx_cnt;
400610253Sxiuyan.wang@Sun.COM 	ethstat->rx_bigbuf_pool.value.ul =
400710253Sxiuyan.wang@Sun.COM 	    ss->jpool.num_alloc - ss->jbufs_for_smalls;
400810253Sxiuyan.wang@Sun.COM 	ethstat->rx_bigbuf_smalls.value.ul = ss->jbufs_for_smalls;
400910253Sxiuyan.wang@Sun.COM 	ethstat->rx_small.value.ul = ss->rx_small.cnt -
401010253Sxiuyan.wang@Sun.COM 	    (ss->rx_small.mask + 1);
401110253Sxiuyan.wang@Sun.COM 	ethstat->tx_done.value.ul = ss->tx.done;
401210253Sxiuyan.wang@Sun.COM 	ethstat->tx_req.value.ul = ss->tx.req;
401310253Sxiuyan.wang@Sun.COM 	ethstat->tx_activate.value.ul = ss->tx.activate;
401410253Sxiuyan.wang@Sun.COM 	ethstat->xmit_sched.value.ul = ss->tx.sched;
401510253Sxiuyan.wang@Sun.COM 	ethstat->xmit_stall.value.ul = ss->tx.stall;
401610253Sxiuyan.wang@Sun.COM 	ethstat->xmit_stall_early.value.ul = ss->tx.stall_early;
401710253Sxiuyan.wang@Sun.COM 	ethstat->xmit_stall_late.value.ul = ss->tx.stall_late;
401810253Sxiuyan.wang@Sun.COM 	ethstat->xmit_err.value.ul =  MYRI10GE_SLICE_STAT(xmit_err);
401910253Sxiuyan.wang@Sun.COM 	return (0);
402010253Sxiuyan.wang@Sun.COM }
402110253Sxiuyan.wang@Sun.COM 
402210253Sxiuyan.wang@Sun.COM static int
myri10ge_info_kstat_update(kstat_t * ksp,int rw)402310253Sxiuyan.wang@Sun.COM myri10ge_info_kstat_update(kstat_t *ksp, int rw)
402410253Sxiuyan.wang@Sun.COM {
402510253Sxiuyan.wang@Sun.COM 	struct myri10ge_info *info;
402610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
402710253Sxiuyan.wang@Sun.COM 
402810253Sxiuyan.wang@Sun.COM 
402910253Sxiuyan.wang@Sun.COM 	if (rw == KSTAT_WRITE)
403010253Sxiuyan.wang@Sun.COM 		return (EACCES);
403110253Sxiuyan.wang@Sun.COM 
403210253Sxiuyan.wang@Sun.COM 	info = (struct myri10ge_info *)ksp->ks_data;
403310253Sxiuyan.wang@Sun.COM 	mgp = (struct myri10ge_priv *)ksp->ks_private;
403410253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->driver_version, MYRI10GE_VERSION_STR);
403510253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->firmware_version, mgp->fw_version);
403610253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->firmware_name, mgp->fw_name);
403710253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->interrupt_type, mgp->intr_type);
403810253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->product_code, mgp->pc_str);
403910253Sxiuyan.wang@Sun.COM 	kstat_named_setstr(&info->serial_number, mgp->sn_str);
404010253Sxiuyan.wang@Sun.COM 	return (0);
404110253Sxiuyan.wang@Sun.COM }
404210253Sxiuyan.wang@Sun.COM 
404310253Sxiuyan.wang@Sun.COM static struct myri10ge_info myri10ge_info_template = {
404410253Sxiuyan.wang@Sun.COM 	{ "driver_version",	KSTAT_DATA_STRING },
404510253Sxiuyan.wang@Sun.COM 	{ "firmware_version",	KSTAT_DATA_STRING },
404610253Sxiuyan.wang@Sun.COM 	{ "firmware_name",	KSTAT_DATA_STRING },
404710253Sxiuyan.wang@Sun.COM 	{ "interrupt_type",	KSTAT_DATA_STRING },
404810253Sxiuyan.wang@Sun.COM 	{ "product_code",	KSTAT_DATA_STRING },
404910253Sxiuyan.wang@Sun.COM 	{ "serial_number",	KSTAT_DATA_STRING },
405010253Sxiuyan.wang@Sun.COM };
405110253Sxiuyan.wang@Sun.COM static kmutex_t myri10ge_info_template_lock;
405210253Sxiuyan.wang@Sun.COM 
405310253Sxiuyan.wang@Sun.COM 
405410253Sxiuyan.wang@Sun.COM static int
myri10ge_info_init(struct myri10ge_priv * mgp)405510253Sxiuyan.wang@Sun.COM myri10ge_info_init(struct myri10ge_priv *mgp)
405610253Sxiuyan.wang@Sun.COM {
405710253Sxiuyan.wang@Sun.COM 	struct kstat *ksp;
405810253Sxiuyan.wang@Sun.COM 
405910253Sxiuyan.wang@Sun.COM 	ksp = kstat_create("myri10ge", ddi_get_instance(mgp->dip),
406010253Sxiuyan.wang@Sun.COM 	    "myri10ge_info", "net", KSTAT_TYPE_NAMED,
406110253Sxiuyan.wang@Sun.COM 	    sizeof (myri10ge_info_template) /
406210253Sxiuyan.wang@Sun.COM 	    sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
406310253Sxiuyan.wang@Sun.COM 	if (ksp == NULL) {
406410253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
406510253Sxiuyan.wang@Sun.COM 		    "%s: myri10ge_info_init: kstat_create failed", mgp->name);
406610253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
406710253Sxiuyan.wang@Sun.COM 	}
406810253Sxiuyan.wang@Sun.COM 	mgp->ksp_info = ksp;
406910253Sxiuyan.wang@Sun.COM 	ksp->ks_update = myri10ge_info_kstat_update;
407010253Sxiuyan.wang@Sun.COM 	ksp->ks_private = (void *) mgp;
407110253Sxiuyan.wang@Sun.COM 	ksp->ks_data = &myri10ge_info_template;
407210253Sxiuyan.wang@Sun.COM 	ksp->ks_lock = &myri10ge_info_template_lock;
407310253Sxiuyan.wang@Sun.COM 	if (MYRI10GE_VERSION_STR != NULL)
407410253Sxiuyan.wang@Sun.COM 		ksp->ks_data_size += strlen(MYRI10GE_VERSION_STR) + 1;
407510253Sxiuyan.wang@Sun.COM 	if (mgp->fw_version != NULL)
407610253Sxiuyan.wang@Sun.COM 		ksp->ks_data_size += strlen(mgp->fw_version) + 1;
407710253Sxiuyan.wang@Sun.COM 	ksp->ks_data_size += strlen(mgp->fw_name) + 1;
407810253Sxiuyan.wang@Sun.COM 	ksp->ks_data_size += strlen(mgp->intr_type) + 1;
407910253Sxiuyan.wang@Sun.COM 	if (mgp->pc_str != NULL)
408010253Sxiuyan.wang@Sun.COM 		ksp->ks_data_size += strlen(mgp->pc_str) + 1;
408110253Sxiuyan.wang@Sun.COM 	if (mgp->sn_str != NULL)
408210253Sxiuyan.wang@Sun.COM 		ksp->ks_data_size += strlen(mgp->sn_str) + 1;
408310253Sxiuyan.wang@Sun.COM 
408410253Sxiuyan.wang@Sun.COM 	kstat_install(ksp);
408510253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
408610253Sxiuyan.wang@Sun.COM }
408710253Sxiuyan.wang@Sun.COM 
408810253Sxiuyan.wang@Sun.COM 
408910253Sxiuyan.wang@Sun.COM static int
myri10ge_nic_stat_init(struct myri10ge_priv * mgp)409010253Sxiuyan.wang@Sun.COM myri10ge_nic_stat_init(struct myri10ge_priv *mgp)
409110253Sxiuyan.wang@Sun.COM {
409210253Sxiuyan.wang@Sun.COM 	struct kstat *ksp;
409310253Sxiuyan.wang@Sun.COM 	struct myri10ge_nic_stat *ethstat;
409410253Sxiuyan.wang@Sun.COM 
409510253Sxiuyan.wang@Sun.COM 	ksp = kstat_create("myri10ge", ddi_get_instance(mgp->dip),
409610253Sxiuyan.wang@Sun.COM 	    "myri10ge_nic_stats", "net", KSTAT_TYPE_NAMED,
409710253Sxiuyan.wang@Sun.COM 	    sizeof (*ethstat) / sizeof (kstat_named_t), 0);
409810253Sxiuyan.wang@Sun.COM 	if (ksp == NULL) {
409910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
410010253Sxiuyan.wang@Sun.COM 		    "%s: myri10ge_stat_init: kstat_create failed", mgp->name);
410110253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
410210253Sxiuyan.wang@Sun.COM 	}
410310253Sxiuyan.wang@Sun.COM 	mgp->ksp_stat = ksp;
410410253Sxiuyan.wang@Sun.COM 	ethstat = (struct myri10ge_nic_stat *)(ksp->ks_data);
410510253Sxiuyan.wang@Sun.COM 
410610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dma_read_bw_MBs,
410710253Sxiuyan.wang@Sun.COM 	    "dma_read_bw_MBs", KSTAT_DATA_ULONG);
410810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dma_write_bw_MBs,
410910253Sxiuyan.wang@Sun.COM 	    "dma_write_bw_MBs", KSTAT_DATA_ULONG);
411010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dma_read_write_bw_MBs,
411110253Sxiuyan.wang@Sun.COM 	    "dma_read_write_bw_MBs", KSTAT_DATA_ULONG);
411210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dma_force_physical,
411310253Sxiuyan.wang@Sun.COM 	    "dma_force_physical", KSTAT_DATA_ULONG);
411410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->lanes,
411510253Sxiuyan.wang@Sun.COM 	    "lanes", KSTAT_DATA_ULONG);
411610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_bad_crc32,
411710253Sxiuyan.wang@Sun.COM 	    "dropped_bad_crc32", KSTAT_DATA_ULONG);
411810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_bad_phy,
411910253Sxiuyan.wang@Sun.COM 	    "dropped_bad_phy", KSTAT_DATA_ULONG);
412010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_link_error_or_filtered,
412110253Sxiuyan.wang@Sun.COM 	    "dropped_link_error_or_filtered", KSTAT_DATA_ULONG);
412210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_link_overflow,
412310253Sxiuyan.wang@Sun.COM 	    "dropped_link_overflow", KSTAT_DATA_ULONG);
412410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_multicast_filtered,
412510253Sxiuyan.wang@Sun.COM 	    "dropped_multicast_filtered", KSTAT_DATA_ULONG);
412610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_no_big_buffer,
412710253Sxiuyan.wang@Sun.COM 	    "dropped_no_big_buffer", KSTAT_DATA_ULONG);
412810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_no_small_buffer,
412910253Sxiuyan.wang@Sun.COM 	    "dropped_no_small_buffer", KSTAT_DATA_ULONG);
413010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_overrun,
413110253Sxiuyan.wang@Sun.COM 	    "dropped_overrun", KSTAT_DATA_ULONG);
413210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_pause,
413310253Sxiuyan.wang@Sun.COM 	    "dropped_pause", KSTAT_DATA_ULONG);
413410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_runt,
413510253Sxiuyan.wang@Sun.COM 	    "dropped_runt", KSTAT_DATA_ULONG);
413610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_unicast_filtered,
413710253Sxiuyan.wang@Sun.COM 	    "dropped_unicast_filtered", KSTAT_DATA_ULONG);
413810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->dropped_runt, "dropped_runt",
413910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
414010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->link_up, "link_up", KSTAT_DATA_ULONG);
414110253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->link_changes, "link_changes",
414210253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
414310253Sxiuyan.wang@Sun.COM 	ksp->ks_update = myri10ge_nic_stat_kstat_update;
414410253Sxiuyan.wang@Sun.COM 	ksp->ks_private = (void *) mgp;
414510253Sxiuyan.wang@Sun.COM 	kstat_install(ksp);
414610253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
414710253Sxiuyan.wang@Sun.COM }
414810253Sxiuyan.wang@Sun.COM 
414910253Sxiuyan.wang@Sun.COM static int
myri10ge_slice_stat_init(struct myri10ge_slice_state * ss)415010253Sxiuyan.wang@Sun.COM myri10ge_slice_stat_init(struct myri10ge_slice_state *ss)
415110253Sxiuyan.wang@Sun.COM {
415210253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ss->mgp;
415310253Sxiuyan.wang@Sun.COM 	struct kstat *ksp;
415410253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_stat *ethstat;
415510253Sxiuyan.wang@Sun.COM 	int instance;
415610253Sxiuyan.wang@Sun.COM 
415710253Sxiuyan.wang@Sun.COM 	/*
415810253Sxiuyan.wang@Sun.COM 	 * fake an instance so that the same slice numbers from
415910253Sxiuyan.wang@Sun.COM 	 * different instances do not collide
416010253Sxiuyan.wang@Sun.COM 	 */
416110253Sxiuyan.wang@Sun.COM 	instance = (ddi_get_instance(mgp->dip) * 1000) +  (int)(ss - mgp->ss);
416210253Sxiuyan.wang@Sun.COM 	ksp = kstat_create("myri10ge", instance,
416310253Sxiuyan.wang@Sun.COM 	    "myri10ge_slice_stats", "net", KSTAT_TYPE_NAMED,
416410253Sxiuyan.wang@Sun.COM 	    sizeof (*ethstat) / sizeof (kstat_named_t), 0);
416510253Sxiuyan.wang@Sun.COM 	if (ksp == NULL) {
416610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
416710253Sxiuyan.wang@Sun.COM 		    "%s: myri10ge_stat_init: kstat_create failed", mgp->name);
416810253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
416910253Sxiuyan.wang@Sun.COM 	}
417010253Sxiuyan.wang@Sun.COM 	ss->ksp_stat = ksp;
417110253Sxiuyan.wang@Sun.COM 	ethstat = (struct myri10ge_slice_stat *)(ksp->ks_data);
417210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->lro_bad_csum, "lro_bad_csum",
417310253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
417410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->lro_flushed, "lro_flushed",
417510253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
417610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->lro_queued, "lro_queued",
417710253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
417810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_bigbuf_firmware, "rx_bigbuf_firmware",
417910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
418010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_bigbuf_pool, "rx_bigbuf_pool",
418110253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
418210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_bigbuf_smalls, "rx_bigbuf_smalls",
418310253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
418410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_copy, "rx_copy",
418510253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
418610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_big_nobuf, "rx_big_nobuf",
418710253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
418810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_small_nobuf, "rx_small_nobuf",
418910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
419010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_zero_len, "xmit_zero_len",
419110253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
419210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_pullup, "xmit_pullup",
419310253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
419410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_pullup_first, "xmit_pullup_first",
419510253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
419610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_lowbuf, "xmit_lowbuf",
419710253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
419810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_lsobadflags, "xmit_lsobadflags",
419910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
420010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_sched, "xmit_sched",
420110253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
420210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_stall, "xmit_stall",
420310253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
420410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_stall_early, "xmit_stall_early",
420510253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
420610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_stall_late, "xmit_stall_late",
420710253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
420810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->xmit_err, "xmit_err",
420910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
421010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->tx_req, "tx_req",
421110253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
421210253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->tx_activate, "tx_activate",
421310253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
421410253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->tx_done, "tx_done",
421510253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
421610253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->tx_handles_alloced, "tx_handles_alloced",
421710253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
421810253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_big, "rx_big",
421910253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
422010253Sxiuyan.wang@Sun.COM 	kstat_named_init(&ethstat->rx_small, "rx_small",
422110253Sxiuyan.wang@Sun.COM 	    KSTAT_DATA_ULONG);
422210253Sxiuyan.wang@Sun.COM 	ksp->ks_update = myri10ge_slice_stat_kstat_update;
422310253Sxiuyan.wang@Sun.COM 	ksp->ks_private = (void *) ss;
422410253Sxiuyan.wang@Sun.COM 	kstat_install(ksp);
422510253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
422610253Sxiuyan.wang@Sun.COM }
422710253Sxiuyan.wang@Sun.COM 
422810253Sxiuyan.wang@Sun.COM 
422910253Sxiuyan.wang@Sun.COM 
423010253Sxiuyan.wang@Sun.COM #if #cpu(i386) || defined __i386 || defined i386 ||	\
423110253Sxiuyan.wang@Sun.COM 	defined __i386__ || #cpu(x86_64) || defined __x86_64__
423210253Sxiuyan.wang@Sun.COM 
423310253Sxiuyan.wang@Sun.COM #include <vm/hat.h>
423411433SMark.Johnson@Sun.COM #include <sys/ddi_isa.h>
423510253Sxiuyan.wang@Sun.COM void *device_arena_alloc(size_t size, int vm_flag);
423610253Sxiuyan.wang@Sun.COM void device_arena_free(void *vaddr, size_t size);
423710253Sxiuyan.wang@Sun.COM 
423810253Sxiuyan.wang@Sun.COM static void
myri10ge_enable_nvidia_ecrc(struct myri10ge_priv * mgp)423910253Sxiuyan.wang@Sun.COM myri10ge_enable_nvidia_ecrc(struct myri10ge_priv *mgp)
424010253Sxiuyan.wang@Sun.COM {
424110253Sxiuyan.wang@Sun.COM 	dev_info_t *parent_dip;
424210253Sxiuyan.wang@Sun.COM 	ddi_acc_handle_t handle;
424310253Sxiuyan.wang@Sun.COM 	unsigned long bus_number, dev_number, func_number;
424410253Sxiuyan.wang@Sun.COM 	unsigned long cfg_pa, paddr, base, pgoffset;
424510253Sxiuyan.wang@Sun.COM 	char 		*cvaddr, *ptr;
424610253Sxiuyan.wang@Sun.COM 	uint32_t	*ptr32;
424710253Sxiuyan.wang@Sun.COM 	int 		retval = DDI_FAILURE;
424810253Sxiuyan.wang@Sun.COM 	int dontcare;
424910253Sxiuyan.wang@Sun.COM 	uint16_t read_vid, read_did, vendor_id, device_id;
425010253Sxiuyan.wang@Sun.COM 
425110253Sxiuyan.wang@Sun.COM 	if (!myri10ge_nvidia_ecrc_enable)
425210253Sxiuyan.wang@Sun.COM 		return;
425310253Sxiuyan.wang@Sun.COM 
425410253Sxiuyan.wang@Sun.COM 	parent_dip = ddi_get_parent(mgp->dip);
425510253Sxiuyan.wang@Sun.COM 	if (parent_dip == NULL) {
425610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: I'm an orphan?", mgp->name);
425710253Sxiuyan.wang@Sun.COM 		return;
425810253Sxiuyan.wang@Sun.COM 	}
425910253Sxiuyan.wang@Sun.COM 
426010253Sxiuyan.wang@Sun.COM 	if (pci_config_setup(parent_dip, &handle) != DDI_SUCCESS) {
426110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
426210253Sxiuyan.wang@Sun.COM 		    "%s: Could not access my parent's registers", mgp->name);
426310253Sxiuyan.wang@Sun.COM 		return;
426410253Sxiuyan.wang@Sun.COM 	}
426510253Sxiuyan.wang@Sun.COM 
426610253Sxiuyan.wang@Sun.COM 	vendor_id = pci_config_get16(handle, PCI_CONF_VENID);
426710253Sxiuyan.wang@Sun.COM 	device_id = pci_config_get16(handle, PCI_CONF_DEVID);
426810253Sxiuyan.wang@Sun.COM 	pci_config_teardown(&handle);
426910253Sxiuyan.wang@Sun.COM 
427010253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose) {
427110253Sxiuyan.wang@Sun.COM 		unsigned long 	bus_number, dev_number, func_number;
427210253Sxiuyan.wang@Sun.COM 		int 		reg_set, span;
427310253Sxiuyan.wang@Sun.COM 		(void) myri10ge_reg_set(parent_dip, &reg_set, &span,
427410253Sxiuyan.wang@Sun.COM 		    &bus_number, &dev_number, &func_number);
427510253Sxiuyan.wang@Sun.COM 		if (myri10ge_verbose)
427610253Sxiuyan.wang@Sun.COM 			printf("%s: parent at %ld:%ld:%ld\n", mgp->name,
427710253Sxiuyan.wang@Sun.COM 			    bus_number, dev_number, func_number);
427810253Sxiuyan.wang@Sun.COM 	}
427910253Sxiuyan.wang@Sun.COM 
428010253Sxiuyan.wang@Sun.COM 	if (vendor_id !=  0x10de)
428110253Sxiuyan.wang@Sun.COM 		return;
428210253Sxiuyan.wang@Sun.COM 
428310253Sxiuyan.wang@Sun.COM 	if (device_id != 0x005d /* CK804 */ &&
428410253Sxiuyan.wang@Sun.COM 	    (device_id < 0x374 || device_id > 0x378) /* MCP55 */) {
428510253Sxiuyan.wang@Sun.COM 		return;
428610253Sxiuyan.wang@Sun.COM 	}
428710253Sxiuyan.wang@Sun.COM 	(void) myri10ge_reg_set(parent_dip, &dontcare, &dontcare,
428810253Sxiuyan.wang@Sun.COM 	    &bus_number, &dev_number, &func_number);
428910253Sxiuyan.wang@Sun.COM 
429010253Sxiuyan.wang@Sun.COM 	for (cfg_pa = 0xf0000000UL;
429110253Sxiuyan.wang@Sun.COM 	    retval != DDI_SUCCESS && cfg_pa >= 0xe0000000UL;
429210253Sxiuyan.wang@Sun.COM 	    cfg_pa -= 0x10000000UL) {
429310253Sxiuyan.wang@Sun.COM 		/* find the config space address for the nvidia bridge */
429410253Sxiuyan.wang@Sun.COM 		paddr = (cfg_pa + bus_number * 0x00100000UL +
429510253Sxiuyan.wang@Sun.COM 		    (dev_number * 8 + func_number) * 0x00001000UL);
429610253Sxiuyan.wang@Sun.COM 
429710253Sxiuyan.wang@Sun.COM 		base = paddr & (~MMU_PAGEOFFSET);
429810253Sxiuyan.wang@Sun.COM 		pgoffset = paddr & MMU_PAGEOFFSET;
429910253Sxiuyan.wang@Sun.COM 
430010253Sxiuyan.wang@Sun.COM 		/* map it into the kernel */
430110253Sxiuyan.wang@Sun.COM 		cvaddr =  device_arena_alloc(ptob(1), VM_NOSLEEP);
430210253Sxiuyan.wang@Sun.COM 		if (cvaddr == NULL)
430310253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN, "%s: failed to map nf4: cvaddr\n",
430410253Sxiuyan.wang@Sun.COM 			    mgp->name);
430510253Sxiuyan.wang@Sun.COM 
430611433SMark.Johnson@Sun.COM 		hat_devload(kas.a_hat, cvaddr, mmu_ptob(1),
430711433SMark.Johnson@Sun.COM 		    i_ddi_paddr_to_pfn(base),
430810253Sxiuyan.wang@Sun.COM 		    PROT_WRITE|HAT_STRICTORDER, HAT_LOAD_LOCK);
430910253Sxiuyan.wang@Sun.COM 
431010253Sxiuyan.wang@Sun.COM 		ptr = cvaddr + pgoffset;
431110253Sxiuyan.wang@Sun.COM 		read_vid = *(uint16_t *)(void *)(ptr + PCI_CONF_VENID);
431210253Sxiuyan.wang@Sun.COM 		read_did = *(uint16_t *)(void *)(ptr + PCI_CONF_DEVID);
431310253Sxiuyan.wang@Sun.COM 		if (vendor_id ==  read_did || device_id == read_did) {
431410253Sxiuyan.wang@Sun.COM 			ptr32 = (uint32_t *)(void *)(ptr + 0x178);
431510253Sxiuyan.wang@Sun.COM 			if (myri10ge_verbose)
431610253Sxiuyan.wang@Sun.COM 				printf("%s: Enabling ECRC on upstream "
431710253Sxiuyan.wang@Sun.COM 				    "Nvidia bridge (0x%x:0x%x) "
431810253Sxiuyan.wang@Sun.COM 				    "at %ld:%ld:%ld\n", mgp->name,
431910253Sxiuyan.wang@Sun.COM 				    read_vid, read_did, bus_number,
432010253Sxiuyan.wang@Sun.COM 				    dev_number, func_number);
432110253Sxiuyan.wang@Sun.COM 			*ptr32 |= 0x40;
432210253Sxiuyan.wang@Sun.COM 			retval = DDI_SUCCESS;
432310253Sxiuyan.wang@Sun.COM 		}
432410253Sxiuyan.wang@Sun.COM 		hat_unload(kas.a_hat, cvaddr, ptob(1), HAT_UNLOAD_UNLOCK);
432510253Sxiuyan.wang@Sun.COM 		device_arena_free(cvaddr, ptob(1));
432610253Sxiuyan.wang@Sun.COM 	}
432710253Sxiuyan.wang@Sun.COM }
432810253Sxiuyan.wang@Sun.COM 
432910253Sxiuyan.wang@Sun.COM #else
433010253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
433110253Sxiuyan.wang@Sun.COM static void
myri10ge_enable_nvidia_ecrc(struct myri10ge_priv * mgp)433210253Sxiuyan.wang@Sun.COM myri10ge_enable_nvidia_ecrc(struct myri10ge_priv *mgp)
433310253Sxiuyan.wang@Sun.COM {
433410253Sxiuyan.wang@Sun.COM }
433510253Sxiuyan.wang@Sun.COM #endif /* i386 */
433610253Sxiuyan.wang@Sun.COM 
433710253Sxiuyan.wang@Sun.COM 
433810253Sxiuyan.wang@Sun.COM /*
433910253Sxiuyan.wang@Sun.COM  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
434010253Sxiuyan.wang@Sun.COM  * when the PCI-E Completion packets are aligned on an 8-byte
434110253Sxiuyan.wang@Sun.COM  * boundary.  Some PCI-E chip sets always align Completion packets; on
434210253Sxiuyan.wang@Sun.COM  * the ones that do not, the alignment can be enforced by enabling
434310253Sxiuyan.wang@Sun.COM  * ECRC generation (if supported).
434410253Sxiuyan.wang@Sun.COM  *
434510253Sxiuyan.wang@Sun.COM  * When PCI-E Completion packets are not aligned, it is actually more
434610253Sxiuyan.wang@Sun.COM  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
434710253Sxiuyan.wang@Sun.COM  *
434810253Sxiuyan.wang@Sun.COM  * If the driver can neither enable ECRC nor verify that it has
434910253Sxiuyan.wang@Sun.COM  * already been enabled, then it must use a firmware image which works
435010253Sxiuyan.wang@Sun.COM  * around unaligned completion packets (ethp_z8e.dat), and it should
435110253Sxiuyan.wang@Sun.COM  * also ensure that it never gives the device a Read-DMA which is
435210253Sxiuyan.wang@Sun.COM  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
435310253Sxiuyan.wang@Sun.COM  * enabled, then the driver should use the aligned (eth_z8e.dat)
435410253Sxiuyan.wang@Sun.COM  * firmware image, and set tx.boundary to 4KB.
435510253Sxiuyan.wang@Sun.COM  */
435610253Sxiuyan.wang@Sun.COM 
435710253Sxiuyan.wang@Sun.COM 
435810253Sxiuyan.wang@Sun.COM static int
myri10ge_firmware_probe(struct myri10ge_priv * mgp)435910253Sxiuyan.wang@Sun.COM myri10ge_firmware_probe(struct myri10ge_priv *mgp)
436010253Sxiuyan.wang@Sun.COM {
436110253Sxiuyan.wang@Sun.COM 	int status;
436210253Sxiuyan.wang@Sun.COM 
436310253Sxiuyan.wang@Sun.COM 	mgp->tx_boundary = 4096;
436410253Sxiuyan.wang@Sun.COM 	/*
436510253Sxiuyan.wang@Sun.COM 	 * Verify the max read request size was set to 4KB
436610253Sxiuyan.wang@Sun.COM 	 * before trying the test with 4KB.
436710253Sxiuyan.wang@Sun.COM 	 */
436810253Sxiuyan.wang@Sun.COM 	if (mgp->max_read_request_4k == 0)
436910253Sxiuyan.wang@Sun.COM 		mgp->tx_boundary = 2048;
437010253Sxiuyan.wang@Sun.COM 	/*
437110253Sxiuyan.wang@Sun.COM 	 * load the optimized firmware which assumes aligned PCIe
437210253Sxiuyan.wang@Sun.COM 	 * completions in order to see if it works on this host.
437310253Sxiuyan.wang@Sun.COM 	 */
437410253Sxiuyan.wang@Sun.COM 
437510253Sxiuyan.wang@Sun.COM 	mgp->fw_name = "rss_eth_z8e";
437610253Sxiuyan.wang@Sun.COM 	mgp->eth_z8e = (unsigned char *)rss_eth_z8e;
437710253Sxiuyan.wang@Sun.COM 	mgp->eth_z8e_length = rss_eth_z8e_length;
437810253Sxiuyan.wang@Sun.COM 
437910253Sxiuyan.wang@Sun.COM 	status = myri10ge_load_firmware(mgp);
438010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
438110253Sxiuyan.wang@Sun.COM 		return (status);
438210253Sxiuyan.wang@Sun.COM 	}
438310253Sxiuyan.wang@Sun.COM 	/*
438410253Sxiuyan.wang@Sun.COM 	 * Enable ECRC if possible
438510253Sxiuyan.wang@Sun.COM 	 */
438610253Sxiuyan.wang@Sun.COM 	myri10ge_enable_nvidia_ecrc(mgp);
438710253Sxiuyan.wang@Sun.COM 
438810253Sxiuyan.wang@Sun.COM 	/*
438910253Sxiuyan.wang@Sun.COM 	 * Run a DMA test which watches for unaligned completions and
439010253Sxiuyan.wang@Sun.COM 	 * aborts on the first one seen.
439110253Sxiuyan.wang@Sun.COM 	 */
439210253Sxiuyan.wang@Sun.COM 	status = myri10ge_dma_test(mgp, MXGEFW_CMD_UNALIGNED_TEST);
439310253Sxiuyan.wang@Sun.COM 	if (status == 0)
439410253Sxiuyan.wang@Sun.COM 		return (0); /* keep the aligned firmware */
439510253Sxiuyan.wang@Sun.COM 
439610253Sxiuyan.wang@Sun.COM 	if (status != E2BIG)
439710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: DMA test failed: %d\n",
439810253Sxiuyan.wang@Sun.COM 		    mgp->name, status);
439910253Sxiuyan.wang@Sun.COM 	if (status == ENOSYS)
440010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Falling back to ethp! "
440110253Sxiuyan.wang@Sun.COM 		    "Please install up to date fw\n", mgp->name);
440210253Sxiuyan.wang@Sun.COM 	return (status);
440310253Sxiuyan.wang@Sun.COM }
440410253Sxiuyan.wang@Sun.COM 
440510253Sxiuyan.wang@Sun.COM static int
myri10ge_select_firmware(struct myri10ge_priv * mgp)440610253Sxiuyan.wang@Sun.COM myri10ge_select_firmware(struct myri10ge_priv *mgp)
440710253Sxiuyan.wang@Sun.COM {
440810253Sxiuyan.wang@Sun.COM 	int aligned;
440910253Sxiuyan.wang@Sun.COM 
441010253Sxiuyan.wang@Sun.COM 	aligned = 0;
441110253Sxiuyan.wang@Sun.COM 
441210253Sxiuyan.wang@Sun.COM 	if (myri10ge_force_firmware == 1) {
441310253Sxiuyan.wang@Sun.COM 		if (myri10ge_verbose)
441410253Sxiuyan.wang@Sun.COM 			printf("%s: Assuming aligned completions (forced)\n",
441510253Sxiuyan.wang@Sun.COM 			    mgp->name);
441610253Sxiuyan.wang@Sun.COM 		aligned = 1;
441710253Sxiuyan.wang@Sun.COM 		goto done;
441810253Sxiuyan.wang@Sun.COM 	}
441910253Sxiuyan.wang@Sun.COM 
442010253Sxiuyan.wang@Sun.COM 	if (myri10ge_force_firmware == 2) {
442110253Sxiuyan.wang@Sun.COM 		if (myri10ge_verbose)
442210253Sxiuyan.wang@Sun.COM 			printf("%s: Assuming unaligned completions (forced)\n",
442310253Sxiuyan.wang@Sun.COM 			    mgp->name);
442410253Sxiuyan.wang@Sun.COM 		aligned = 0;
442510253Sxiuyan.wang@Sun.COM 		goto done;
442610253Sxiuyan.wang@Sun.COM 	}
442710253Sxiuyan.wang@Sun.COM 
442810253Sxiuyan.wang@Sun.COM 	/* If the width is less than 8, we may used the aligned firmware */
442910253Sxiuyan.wang@Sun.COM 	if (mgp->pcie_link_width != 0 && mgp->pcie_link_width < 8) {
443010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "!%s: PCIe link running at x%d\n",
443110253Sxiuyan.wang@Sun.COM 		    mgp->name, mgp->pcie_link_width);
443210253Sxiuyan.wang@Sun.COM 		aligned = 1;
443310253Sxiuyan.wang@Sun.COM 		goto done;
443410253Sxiuyan.wang@Sun.COM 	}
443510253Sxiuyan.wang@Sun.COM 
443610253Sxiuyan.wang@Sun.COM 	if (0 == myri10ge_firmware_probe(mgp))
443710253Sxiuyan.wang@Sun.COM 		return (0);  /* keep optimized firmware */
443810253Sxiuyan.wang@Sun.COM 
443910253Sxiuyan.wang@Sun.COM done:
444010253Sxiuyan.wang@Sun.COM 	if (aligned) {
444110253Sxiuyan.wang@Sun.COM 		mgp->fw_name = "rss_eth_z8e";
444210253Sxiuyan.wang@Sun.COM 		mgp->eth_z8e = (unsigned char *)rss_eth_z8e;
444310253Sxiuyan.wang@Sun.COM 		mgp->eth_z8e_length = rss_eth_z8e_length;
444410253Sxiuyan.wang@Sun.COM 		mgp->tx_boundary = 4096;
444510253Sxiuyan.wang@Sun.COM 	} else {
444610253Sxiuyan.wang@Sun.COM 		mgp->fw_name = "rss_ethp_z8e";
444710253Sxiuyan.wang@Sun.COM 		mgp->eth_z8e = (unsigned char *)rss_ethp_z8e;
444810253Sxiuyan.wang@Sun.COM 		mgp->eth_z8e_length = rss_ethp_z8e_length;
444910253Sxiuyan.wang@Sun.COM 		mgp->tx_boundary = 2048;
445010253Sxiuyan.wang@Sun.COM 	}
445110253Sxiuyan.wang@Sun.COM 
445210253Sxiuyan.wang@Sun.COM 	return (myri10ge_load_firmware(mgp));
445310253Sxiuyan.wang@Sun.COM }
445410253Sxiuyan.wang@Sun.COM 
445510253Sxiuyan.wang@Sun.COM static int
myri10ge_add_intrs(struct myri10ge_priv * mgp,int add_handler)445610253Sxiuyan.wang@Sun.COM myri10ge_add_intrs(struct myri10ge_priv *mgp, int add_handler)
445710253Sxiuyan.wang@Sun.COM {
445810253Sxiuyan.wang@Sun.COM 	dev_info_t *devinfo = mgp->dip;
445910253Sxiuyan.wang@Sun.COM 	int count, avail, actual, intr_types;
446010253Sxiuyan.wang@Sun.COM 	int x, y, rc, inum = 0;
446110253Sxiuyan.wang@Sun.COM 
446210253Sxiuyan.wang@Sun.COM 
446310253Sxiuyan.wang@Sun.COM 	rc = ddi_intr_get_supported_types(devinfo, &intr_types);
446410253Sxiuyan.wang@Sun.COM 	if (rc != DDI_SUCCESS) {
446510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
446610253Sxiuyan.wang@Sun.COM 		    "!%s: ddi_intr_get_nintrs() failure, rc = %d\n", mgp->name,
446710253Sxiuyan.wang@Sun.COM 		    rc);
446810253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
446910253Sxiuyan.wang@Sun.COM 	}
447010253Sxiuyan.wang@Sun.COM 
447110253Sxiuyan.wang@Sun.COM 	if (!myri10ge_use_msi)
447210253Sxiuyan.wang@Sun.COM 		intr_types &= ~DDI_INTR_TYPE_MSI;
447310253Sxiuyan.wang@Sun.COM 	if (!myri10ge_use_msix)
447410253Sxiuyan.wang@Sun.COM 		intr_types &= ~DDI_INTR_TYPE_MSIX;
447510253Sxiuyan.wang@Sun.COM 
447610253Sxiuyan.wang@Sun.COM 	if (intr_types & DDI_INTR_TYPE_MSIX) {
447710253Sxiuyan.wang@Sun.COM 		mgp->ddi_intr_type = DDI_INTR_TYPE_MSIX;
447810253Sxiuyan.wang@Sun.COM 		mgp->intr_type = "MSI-X";
447910253Sxiuyan.wang@Sun.COM 	} else if (intr_types & DDI_INTR_TYPE_MSI) {
448010253Sxiuyan.wang@Sun.COM 		mgp->ddi_intr_type = DDI_INTR_TYPE_MSI;
448110253Sxiuyan.wang@Sun.COM 		mgp->intr_type = "MSI";
448210253Sxiuyan.wang@Sun.COM 	} else {
448310253Sxiuyan.wang@Sun.COM 		mgp->ddi_intr_type = DDI_INTR_TYPE_FIXED;
448410253Sxiuyan.wang@Sun.COM 		mgp->intr_type = "Legacy";
448510253Sxiuyan.wang@Sun.COM 	}
448610253Sxiuyan.wang@Sun.COM 	/* Get number of interrupts */
448710253Sxiuyan.wang@Sun.COM 	rc = ddi_intr_get_nintrs(devinfo, mgp->ddi_intr_type, &count);
448810253Sxiuyan.wang@Sun.COM 	if ((rc != DDI_SUCCESS) || (count == 0)) {
448910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_intr_get_nintrs() failure, rc: %d, "
449010253Sxiuyan.wang@Sun.COM 		    "count: %d", mgp->name, rc, count);
449110253Sxiuyan.wang@Sun.COM 
449210253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
449310253Sxiuyan.wang@Sun.COM 	}
449410253Sxiuyan.wang@Sun.COM 
449510253Sxiuyan.wang@Sun.COM 	/* Get number of available interrupts */
449610253Sxiuyan.wang@Sun.COM 	rc = ddi_intr_get_navail(devinfo, mgp->ddi_intr_type, &avail);
449710253Sxiuyan.wang@Sun.COM 	if ((rc != DDI_SUCCESS) || (avail == 0)) {
449810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_intr_get_navail() failure, "
449910253Sxiuyan.wang@Sun.COM 		    "rc: %d, avail: %d\n", mgp->name, rc, avail);
450010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
450110253Sxiuyan.wang@Sun.COM 	}
450210253Sxiuyan.wang@Sun.COM 	if (avail < count) {
450310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_NOTE,
450410253Sxiuyan.wang@Sun.COM 		    "!%s: nintrs() returned %d, navail returned %d",
450510253Sxiuyan.wang@Sun.COM 		    mgp->name, count, avail);
450610253Sxiuyan.wang@Sun.COM 		count = avail;
450710253Sxiuyan.wang@Sun.COM 	}
450810253Sxiuyan.wang@Sun.COM 
450910253Sxiuyan.wang@Sun.COM 	if (count < mgp->num_slices)
451010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
451110253Sxiuyan.wang@Sun.COM 
451210253Sxiuyan.wang@Sun.COM 	if (count > mgp->num_slices)
451310253Sxiuyan.wang@Sun.COM 		count = mgp->num_slices;
451410253Sxiuyan.wang@Sun.COM 
451510253Sxiuyan.wang@Sun.COM 	/* Allocate memory for MSI interrupts */
451610253Sxiuyan.wang@Sun.COM 	mgp->intr_size = count * sizeof (ddi_intr_handle_t);
451710253Sxiuyan.wang@Sun.COM 	mgp->htable = kmem_alloc(mgp->intr_size, KM_SLEEP);
451810253Sxiuyan.wang@Sun.COM 
451910253Sxiuyan.wang@Sun.COM 	rc = ddi_intr_alloc(devinfo, mgp->htable, mgp->ddi_intr_type, inum,
452010253Sxiuyan.wang@Sun.COM 	    count, &actual, DDI_INTR_ALLOC_NORMAL);
452110253Sxiuyan.wang@Sun.COM 
452210253Sxiuyan.wang@Sun.COM 	if ((rc != DDI_SUCCESS) || (actual == 0)) {
452310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_intr_alloc() failed: %d",
452410253Sxiuyan.wang@Sun.COM 		    mgp->name, rc);
452510253Sxiuyan.wang@Sun.COM 
452610253Sxiuyan.wang@Sun.COM 		kmem_free(mgp->htable, mgp->intr_size);
452710253Sxiuyan.wang@Sun.COM 		mgp->htable = NULL;
452810253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
452910253Sxiuyan.wang@Sun.COM 	}
453010253Sxiuyan.wang@Sun.COM 
453110253Sxiuyan.wang@Sun.COM 	if ((actual < count) && myri10ge_verbose) {
453210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_NOTE, "%s: got %d/%d slices",
453310253Sxiuyan.wang@Sun.COM 		    mgp->name, actual, count);
453410253Sxiuyan.wang@Sun.COM 	}
453510253Sxiuyan.wang@Sun.COM 
453610253Sxiuyan.wang@Sun.COM 	mgp->intr_cnt = actual;
453710253Sxiuyan.wang@Sun.COM 
453810253Sxiuyan.wang@Sun.COM 	/*
453910253Sxiuyan.wang@Sun.COM 	 * Get priority for first irq, assume remaining are all the same
454010253Sxiuyan.wang@Sun.COM 	 */
454110253Sxiuyan.wang@Sun.COM 	if (ddi_intr_get_pri(mgp->htable[0], &mgp->intr_pri)
454210253Sxiuyan.wang@Sun.COM 	    != DDI_SUCCESS) {
454310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_intr_get_pri() failed", mgp->name);
454410253Sxiuyan.wang@Sun.COM 
454510253Sxiuyan.wang@Sun.COM 		/* Free already allocated intr */
454610253Sxiuyan.wang@Sun.COM 		for (y = 0; y < actual; y++) {
454710253Sxiuyan.wang@Sun.COM 			(void) ddi_intr_free(mgp->htable[y]);
454810253Sxiuyan.wang@Sun.COM 		}
454910253Sxiuyan.wang@Sun.COM 
455010253Sxiuyan.wang@Sun.COM 		kmem_free(mgp->htable, mgp->intr_size);
455110253Sxiuyan.wang@Sun.COM 		mgp->htable = NULL;
455210253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
455310253Sxiuyan.wang@Sun.COM 	}
455410253Sxiuyan.wang@Sun.COM 
455510253Sxiuyan.wang@Sun.COM 	mgp->icookie = (void *)(uintptr_t)mgp->intr_pri;
455610253Sxiuyan.wang@Sun.COM 
455710253Sxiuyan.wang@Sun.COM 	if (!add_handler)
455810253Sxiuyan.wang@Sun.COM 		return (DDI_SUCCESS);
455910253Sxiuyan.wang@Sun.COM 
456010253Sxiuyan.wang@Sun.COM 	/* Call ddi_intr_add_handler() */
456110253Sxiuyan.wang@Sun.COM 	for (x = 0; x < actual; x++) {
456210253Sxiuyan.wang@Sun.COM 		if (ddi_intr_add_handler(mgp->htable[x], myri10ge_intr,
456310253Sxiuyan.wang@Sun.COM 		    (caddr_t)&mgp->ss[x], NULL) != DDI_SUCCESS) {
456410253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN, "%s: ddi_intr_add_handler() failed",
456510253Sxiuyan.wang@Sun.COM 			    mgp->name);
456610253Sxiuyan.wang@Sun.COM 
456710253Sxiuyan.wang@Sun.COM 			/* Free already allocated intr */
456810253Sxiuyan.wang@Sun.COM 			for (y = 0; y < actual; y++) {
456910253Sxiuyan.wang@Sun.COM 				(void) ddi_intr_free(mgp->htable[y]);
457010253Sxiuyan.wang@Sun.COM 			}
457110253Sxiuyan.wang@Sun.COM 
457210253Sxiuyan.wang@Sun.COM 			kmem_free(mgp->htable, mgp->intr_size);
457310253Sxiuyan.wang@Sun.COM 			mgp->htable = NULL;
457410253Sxiuyan.wang@Sun.COM 			return (DDI_FAILURE);
457510253Sxiuyan.wang@Sun.COM 		}
457610253Sxiuyan.wang@Sun.COM 	}
457710253Sxiuyan.wang@Sun.COM 
457810253Sxiuyan.wang@Sun.COM 	(void) ddi_intr_get_cap(mgp->htable[0], &mgp->intr_cap);
457910253Sxiuyan.wang@Sun.COM 	if (mgp->intr_cap & DDI_INTR_FLAG_BLOCK) {
458010253Sxiuyan.wang@Sun.COM 		/* Call ddi_intr_block_enable() for MSI */
458110253Sxiuyan.wang@Sun.COM 		(void) ddi_intr_block_enable(mgp->htable, mgp->intr_cnt);
458210253Sxiuyan.wang@Sun.COM 	} else {
458310253Sxiuyan.wang@Sun.COM 		/* Call ddi_intr_enable() for MSI non block enable */
458410253Sxiuyan.wang@Sun.COM 		for (x = 0; x < mgp->intr_cnt; x++) {
458510253Sxiuyan.wang@Sun.COM 			(void) ddi_intr_enable(mgp->htable[x]);
458610253Sxiuyan.wang@Sun.COM 		}
458710253Sxiuyan.wang@Sun.COM 	}
458810253Sxiuyan.wang@Sun.COM 
458910253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
459010253Sxiuyan.wang@Sun.COM }
459110253Sxiuyan.wang@Sun.COM 
459210253Sxiuyan.wang@Sun.COM static void
myri10ge_rem_intrs(struct myri10ge_priv * mgp,int handler_installed)459310253Sxiuyan.wang@Sun.COM myri10ge_rem_intrs(struct myri10ge_priv *mgp, int handler_installed)
459410253Sxiuyan.wang@Sun.COM {
459510253Sxiuyan.wang@Sun.COM 	int x, err;
459610253Sxiuyan.wang@Sun.COM 
459710253Sxiuyan.wang@Sun.COM 	/* Disable all interrupts */
459810253Sxiuyan.wang@Sun.COM 	if (handler_installed) {
459910253Sxiuyan.wang@Sun.COM 		if (mgp->intr_cap & DDI_INTR_FLAG_BLOCK) {
460010253Sxiuyan.wang@Sun.COM 			/* Call ddi_intr_block_disable() */
460110253Sxiuyan.wang@Sun.COM 			(void) ddi_intr_block_disable(mgp->htable,
460210253Sxiuyan.wang@Sun.COM 			    mgp->intr_cnt);
460310253Sxiuyan.wang@Sun.COM 		} else {
460410253Sxiuyan.wang@Sun.COM 			for (x = 0; x < mgp->intr_cnt; x++) {
460510253Sxiuyan.wang@Sun.COM 				(void) ddi_intr_disable(mgp->htable[x]);
460610253Sxiuyan.wang@Sun.COM 			}
460710253Sxiuyan.wang@Sun.COM 		}
460810253Sxiuyan.wang@Sun.COM 	}
460910253Sxiuyan.wang@Sun.COM 
461010253Sxiuyan.wang@Sun.COM 	for (x = 0; x < mgp->intr_cnt; x++) {
461110253Sxiuyan.wang@Sun.COM 		if (handler_installed) {
461210253Sxiuyan.wang@Sun.COM 		/* Call ddi_intr_remove_handler() */
461310253Sxiuyan.wang@Sun.COM 			err = ddi_intr_remove_handler(mgp->htable[x]);
461410253Sxiuyan.wang@Sun.COM 			if (err != DDI_SUCCESS) {
461510253Sxiuyan.wang@Sun.COM 				cmn_err(CE_WARN,
461610253Sxiuyan.wang@Sun.COM 				    "%s: ddi_intr_remove_handler for"
461710253Sxiuyan.wang@Sun.COM 				    "vec %d returned %d\n", mgp->name,
461810253Sxiuyan.wang@Sun.COM 				    x, err);
461910253Sxiuyan.wang@Sun.COM 			}
462010253Sxiuyan.wang@Sun.COM 		}
462110253Sxiuyan.wang@Sun.COM 		err = ddi_intr_free(mgp->htable[x]);
462210253Sxiuyan.wang@Sun.COM 		if (err != DDI_SUCCESS) {
462310253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
462410253Sxiuyan.wang@Sun.COM 			    "%s: ddi_intr_free for vec %d returned %d\n",
462510253Sxiuyan.wang@Sun.COM 			    mgp->name, x, err);
462610253Sxiuyan.wang@Sun.COM 		}
462710253Sxiuyan.wang@Sun.COM 	}
462810253Sxiuyan.wang@Sun.COM 	kmem_free(mgp->htable, mgp->intr_size);
462910253Sxiuyan.wang@Sun.COM 	mgp->htable = NULL;
463010253Sxiuyan.wang@Sun.COM }
463110253Sxiuyan.wang@Sun.COM 
463210253Sxiuyan.wang@Sun.COM static void
myri10ge_test_physical(dev_info_t * dip)463310253Sxiuyan.wang@Sun.COM myri10ge_test_physical(dev_info_t *dip)
463410253Sxiuyan.wang@Sun.COM {
463510253Sxiuyan.wang@Sun.COM 	ddi_dma_handle_t	handle;
463610253Sxiuyan.wang@Sun.COM 	struct myri10ge_dma_stuff dma;
463710253Sxiuyan.wang@Sun.COM 	void *addr;
463810253Sxiuyan.wang@Sun.COM 	int err;
463910253Sxiuyan.wang@Sun.COM 
464010253Sxiuyan.wang@Sun.COM 	/* test #1, sufficient for older sparc systems */
464110253Sxiuyan.wang@Sun.COM 	myri10ge_tx_dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
464210253Sxiuyan.wang@Sun.COM 	err = ddi_dma_alloc_handle(dip, &myri10ge_tx_dma_attr,
464310253Sxiuyan.wang@Sun.COM 	    DDI_DMA_DONTWAIT, NULL, &handle);
464410253Sxiuyan.wang@Sun.COM 	if (err == DDI_DMA_BADATTR)
464510253Sxiuyan.wang@Sun.COM 		goto fail;
464610253Sxiuyan.wang@Sun.COM 	ddi_dma_free_handle(&handle);
464710253Sxiuyan.wang@Sun.COM 
464810253Sxiuyan.wang@Sun.COM 	/* test #2, required on Olympis where the bind is what fails */
464910253Sxiuyan.wang@Sun.COM 	addr = myri10ge_dma_alloc(dip, 128, &myri10ge_tx_dma_attr,
465010253Sxiuyan.wang@Sun.COM 	    &myri10ge_dev_access_attr, DDI_DMA_STREAMING,
465110253Sxiuyan.wang@Sun.COM 	    DDI_DMA_WRITE|DDI_DMA_STREAMING, &dma, 0, DDI_DMA_DONTWAIT);
465210253Sxiuyan.wang@Sun.COM 	if (addr == NULL)
465310253Sxiuyan.wang@Sun.COM 		goto fail;
465410253Sxiuyan.wang@Sun.COM 	myri10ge_dma_free(&dma);
465510253Sxiuyan.wang@Sun.COM 	return;
465610253Sxiuyan.wang@Sun.COM 
465710253Sxiuyan.wang@Sun.COM fail:
465810253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
465910253Sxiuyan.wang@Sun.COM 		printf("myri10ge%d: DDI_DMA_FORCE_PHYSICAL failed, "
466010253Sxiuyan.wang@Sun.COM 		    "using IOMMU\n", ddi_get_instance(dip));
466110253Sxiuyan.wang@Sun.COM 
466210253Sxiuyan.wang@Sun.COM 	myri10ge_tx_dma_attr.dma_attr_flags &= ~DDI_DMA_FORCE_PHYSICAL;
466310253Sxiuyan.wang@Sun.COM }
466410253Sxiuyan.wang@Sun.COM 
466510253Sxiuyan.wang@Sun.COM static void
myri10ge_get_props(dev_info_t * dip)466610253Sxiuyan.wang@Sun.COM myri10ge_get_props(dev_info_t *dip)
466710253Sxiuyan.wang@Sun.COM {
466810253Sxiuyan.wang@Sun.COM 
466910253Sxiuyan.wang@Sun.COM 	myri10ge_flow_control =  ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
467010253Sxiuyan.wang@Sun.COM 	    "myri10ge_flow_control", myri10ge_flow_control);
467110253Sxiuyan.wang@Sun.COM 
467210253Sxiuyan.wang@Sun.COM 	myri10ge_intr_coal_delay = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
467310253Sxiuyan.wang@Sun.COM 	    "myri10ge_intr_coal_delay", myri10ge_intr_coal_delay);
467410253Sxiuyan.wang@Sun.COM 
467510253Sxiuyan.wang@Sun.COM #if #cpu(i386) || defined __i386 || defined i386 ||	\
467610253Sxiuyan.wang@Sun.COM 	defined __i386__ || #cpu(x86_64) || defined __x86_64__
467710253Sxiuyan.wang@Sun.COM 	myri10ge_nvidia_ecrc_enable = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
467810253Sxiuyan.wang@Sun.COM 	    "myri10ge_nvidia_ecrc_enable", 1);
467910253Sxiuyan.wang@Sun.COM #endif
468010253Sxiuyan.wang@Sun.COM 
468110253Sxiuyan.wang@Sun.COM 
468210253Sxiuyan.wang@Sun.COM 	myri10ge_use_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
468310253Sxiuyan.wang@Sun.COM 	    "myri10ge_use_msi", myri10ge_use_msi);
468410253Sxiuyan.wang@Sun.COM 
468510253Sxiuyan.wang@Sun.COM 	myri10ge_deassert_wait = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
468610253Sxiuyan.wang@Sun.COM 	    "myri10ge_deassert_wait",  myri10ge_deassert_wait);
468710253Sxiuyan.wang@Sun.COM 
468810253Sxiuyan.wang@Sun.COM 	myri10ge_verbose = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
468910253Sxiuyan.wang@Sun.COM 	    "myri10ge_verbose", myri10ge_verbose);
469010253Sxiuyan.wang@Sun.COM 
469110253Sxiuyan.wang@Sun.COM 	myri10ge_tx_copylen = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
469210253Sxiuyan.wang@Sun.COM 	    "myri10ge_tx_copylen", myri10ge_tx_copylen);
469310253Sxiuyan.wang@Sun.COM 
469410253Sxiuyan.wang@Sun.COM 	if (myri10ge_tx_copylen < 60) {
469510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
469610253Sxiuyan.wang@Sun.COM 		    "myri10ge_tx_copylen must be >= 60 bytes\n");
469710253Sxiuyan.wang@Sun.COM 		myri10ge_tx_copylen = 60;
469810253Sxiuyan.wang@Sun.COM 	}
469910253Sxiuyan.wang@Sun.COM 
470010253Sxiuyan.wang@Sun.COM 	myri10ge_mtu_override = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
470110253Sxiuyan.wang@Sun.COM 	    "myri10ge_mtu_override", myri10ge_mtu_override);
470210253Sxiuyan.wang@Sun.COM 
470310253Sxiuyan.wang@Sun.COM 	if (myri10ge_mtu_override >= 1500 && myri10ge_mtu_override <= 9000)
470410253Sxiuyan.wang@Sun.COM 		myri10ge_mtu = myri10ge_mtu_override +
470510253Sxiuyan.wang@Sun.COM 		    sizeof (struct ether_header) + MXGEFW_PAD + VLAN_TAGSZ;
470610253Sxiuyan.wang@Sun.COM 	else if (myri10ge_mtu_override != 0) {
470710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
470810253Sxiuyan.wang@Sun.COM 		    "myri10ge_mtu_override must be between 1500 and "
470910253Sxiuyan.wang@Sun.COM 		    "9000 bytes\n");
471010253Sxiuyan.wang@Sun.COM 	}
471110253Sxiuyan.wang@Sun.COM 
471210253Sxiuyan.wang@Sun.COM 	myri10ge_bigbufs_initial = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
471310253Sxiuyan.wang@Sun.COM 	    "myri10ge_bigbufs_initial", myri10ge_bigbufs_initial);
471410253Sxiuyan.wang@Sun.COM 	myri10ge_bigbufs_max = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
471510253Sxiuyan.wang@Sun.COM 	    "myri10ge_bigbufs_max", myri10ge_bigbufs_max);
471610253Sxiuyan.wang@Sun.COM 
471710253Sxiuyan.wang@Sun.COM 	myri10ge_watchdog_reset = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
471810253Sxiuyan.wang@Sun.COM 	    "myri10ge_watchdog_reset", myri10ge_watchdog_reset);
471910253Sxiuyan.wang@Sun.COM 
472010253Sxiuyan.wang@Sun.COM 	if (myri10ge_bigbufs_initial < 128) {
472110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
472210253Sxiuyan.wang@Sun.COM 		    "myri10ge_bigbufs_initial be at least 128\n");
472310253Sxiuyan.wang@Sun.COM 		myri10ge_bigbufs_initial = 128;
472410253Sxiuyan.wang@Sun.COM 	}
472510253Sxiuyan.wang@Sun.COM 	if (myri10ge_bigbufs_max < 128) {
472610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
472710253Sxiuyan.wang@Sun.COM 		    "myri10ge_bigbufs_max be at least 128\n");
472810253Sxiuyan.wang@Sun.COM 		myri10ge_bigbufs_max = 128;
472910253Sxiuyan.wang@Sun.COM 	}
473010253Sxiuyan.wang@Sun.COM 
473110253Sxiuyan.wang@Sun.COM 	if (myri10ge_bigbufs_max < myri10ge_bigbufs_initial) {
473210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
473310253Sxiuyan.wang@Sun.COM 		    "myri10ge_bigbufs_max must be >=  "
473410253Sxiuyan.wang@Sun.COM 		    "myri10ge_bigbufs_initial\n");
473510253Sxiuyan.wang@Sun.COM 		myri10ge_bigbufs_max = myri10ge_bigbufs_initial;
473610253Sxiuyan.wang@Sun.COM 	}
473710253Sxiuyan.wang@Sun.COM 
473810253Sxiuyan.wang@Sun.COM 	myri10ge_force_firmware = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
473910253Sxiuyan.wang@Sun.COM 	    "myri10ge_force_firmware", myri10ge_force_firmware);
474010253Sxiuyan.wang@Sun.COM 
474110253Sxiuyan.wang@Sun.COM 	myri10ge_max_slices = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
474210253Sxiuyan.wang@Sun.COM 	    "myri10ge_max_slices", myri10ge_max_slices);
474310253Sxiuyan.wang@Sun.COM 
474410253Sxiuyan.wang@Sun.COM 	myri10ge_use_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
474510253Sxiuyan.wang@Sun.COM 	    "myri10ge_use_msix", myri10ge_use_msix);
474610253Sxiuyan.wang@Sun.COM 
474710253Sxiuyan.wang@Sun.COM 	myri10ge_rss_hash = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
474810253Sxiuyan.wang@Sun.COM 	    "myri10ge_rss_hash", myri10ge_rss_hash);
474910253Sxiuyan.wang@Sun.COM 
475010253Sxiuyan.wang@Sun.COM 	if (myri10ge_rss_hash > MXGEFW_RSS_HASH_TYPE_MAX ||
475110253Sxiuyan.wang@Sun.COM 	    myri10ge_rss_hash < MXGEFW_RSS_HASH_TYPE_IPV4) {
475210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "myri10ge: Illegal rssh hash type %d\n",
475310253Sxiuyan.wang@Sun.COM 		    myri10ge_rss_hash);
475410253Sxiuyan.wang@Sun.COM 		myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
475510253Sxiuyan.wang@Sun.COM 	}
475610253Sxiuyan.wang@Sun.COM 	myri10ge_lro = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
475710253Sxiuyan.wang@Sun.COM 	    "myri10ge_lro", myri10ge_lro);
475810253Sxiuyan.wang@Sun.COM 	myri10ge_lro_cnt = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
475910253Sxiuyan.wang@Sun.COM 	    "myri10ge_lro_cnt", myri10ge_lro_cnt);
476010253Sxiuyan.wang@Sun.COM 	myri10ge_lro_max_aggr = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
476110253Sxiuyan.wang@Sun.COM 	    "myri10ge_lro_max_aggr", myri10ge_lro_max_aggr);
476210253Sxiuyan.wang@Sun.COM 	myri10ge_tx_hash = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
476310253Sxiuyan.wang@Sun.COM 	    "myri10ge_tx_hash", myri10ge_tx_hash);
476410253Sxiuyan.wang@Sun.COM 	myri10ge_use_lso = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
476510253Sxiuyan.wang@Sun.COM 	    "myri10ge_use_lso", myri10ge_use_lso);
476610253Sxiuyan.wang@Sun.COM 	myri10ge_lso_copy = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
476710253Sxiuyan.wang@Sun.COM 	    "myri10ge_lso_copy", myri10ge_lso_copy);
476810253Sxiuyan.wang@Sun.COM 	myri10ge_tx_handles_initial = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
476910253Sxiuyan.wang@Sun.COM 	    "myri10ge_tx_handles_initial", myri10ge_tx_handles_initial);
477010253Sxiuyan.wang@Sun.COM 	myri10ge_small_bytes = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
477110253Sxiuyan.wang@Sun.COM 	    "myri10ge_small_bytes", myri10ge_small_bytes);
477210253Sxiuyan.wang@Sun.COM 	if ((myri10ge_small_bytes + MXGEFW_PAD) & (128 -1)) {
477310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "myri10ge: myri10ge_small_bytes (%d)\n",
477410253Sxiuyan.wang@Sun.COM 		    myri10ge_small_bytes);
477510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "must be aligned on 128b bndry -2\n");
477610253Sxiuyan.wang@Sun.COM 		myri10ge_small_bytes += 128;
477710253Sxiuyan.wang@Sun.COM 		myri10ge_small_bytes &= ~(128 -1);
477810253Sxiuyan.wang@Sun.COM 		myri10ge_small_bytes -= MXGEFW_PAD;
477910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "rounded up to %d\n",
478010253Sxiuyan.wang@Sun.COM 		    myri10ge_small_bytes);
478110253Sxiuyan.wang@Sun.COM 
478210253Sxiuyan.wang@Sun.COM 		myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
478310253Sxiuyan.wang@Sun.COM 	}
478410253Sxiuyan.wang@Sun.COM }
478510253Sxiuyan.wang@Sun.COM 
478610253Sxiuyan.wang@Sun.COM #ifndef	PCI_EXP_LNKSTA
478710253Sxiuyan.wang@Sun.COM #define	PCI_EXP_LNKSTA 18
478810253Sxiuyan.wang@Sun.COM #endif
478910253Sxiuyan.wang@Sun.COM 
479010253Sxiuyan.wang@Sun.COM static int
myri10ge_find_cap(ddi_acc_handle_t handle,uint8_t * capptr,uint8_t capid)479110253Sxiuyan.wang@Sun.COM myri10ge_find_cap(ddi_acc_handle_t handle, uint8_t *capptr, uint8_t capid)
479210253Sxiuyan.wang@Sun.COM {
479310253Sxiuyan.wang@Sun.COM 	uint16_t	status;
479410253Sxiuyan.wang@Sun.COM 	uint8_t 	ptr;
479510253Sxiuyan.wang@Sun.COM 
479610253Sxiuyan.wang@Sun.COM 	/* check to see if we have capabilities */
479710253Sxiuyan.wang@Sun.COM 	status = pci_config_get16(handle, PCI_CONF_STAT);
479810253Sxiuyan.wang@Sun.COM 	if (!(status & PCI_STAT_CAP)) {
479910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "PCI_STAT_CAP not found\n");
480010253Sxiuyan.wang@Sun.COM 		return (ENXIO);
480110253Sxiuyan.wang@Sun.COM 	}
480210253Sxiuyan.wang@Sun.COM 
480310253Sxiuyan.wang@Sun.COM 	ptr = pci_config_get8(handle, PCI_CONF_CAP_PTR);
480410253Sxiuyan.wang@Sun.COM 
480510253Sxiuyan.wang@Sun.COM 	/* Walk the capabilities list, looking for a PCI Express cap */
480610253Sxiuyan.wang@Sun.COM 	while (ptr != PCI_CAP_NEXT_PTR_NULL) {
480710253Sxiuyan.wang@Sun.COM 		if (pci_config_get8(handle, ptr + PCI_CAP_ID) == capid)
480810253Sxiuyan.wang@Sun.COM 			break;
480910253Sxiuyan.wang@Sun.COM 		ptr = pci_config_get8(handle, ptr + PCI_CAP_NEXT_PTR);
481010253Sxiuyan.wang@Sun.COM 	}
481110253Sxiuyan.wang@Sun.COM 	if (ptr < 64) {
481210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "Bad capability offset %d\n", ptr);
481310253Sxiuyan.wang@Sun.COM 		return (ENXIO);
481410253Sxiuyan.wang@Sun.COM 	}
481510253Sxiuyan.wang@Sun.COM 	*capptr = ptr;
481610253Sxiuyan.wang@Sun.COM 	return (0);
481710253Sxiuyan.wang@Sun.COM }
481810253Sxiuyan.wang@Sun.COM 
481910253Sxiuyan.wang@Sun.COM static int
myri10ge_set_max_readreq(ddi_acc_handle_t handle)482010253Sxiuyan.wang@Sun.COM myri10ge_set_max_readreq(ddi_acc_handle_t handle)
482110253Sxiuyan.wang@Sun.COM {
482210253Sxiuyan.wang@Sun.COM 	int err;
482310253Sxiuyan.wang@Sun.COM 	uint16_t	val;
482410253Sxiuyan.wang@Sun.COM 	uint8_t		ptr;
482510253Sxiuyan.wang@Sun.COM 
482610253Sxiuyan.wang@Sun.COM 	err = myri10ge_find_cap(handle, &ptr, PCI_CAP_ID_PCI_E);
482710253Sxiuyan.wang@Sun.COM 	if (err != 0) {
482810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "could not find PCIe cap\n");
482910253Sxiuyan.wang@Sun.COM 		return (ENXIO);
483010253Sxiuyan.wang@Sun.COM 	}
483110253Sxiuyan.wang@Sun.COM 
483210253Sxiuyan.wang@Sun.COM 	/* set max read req to 4096 */
483310253Sxiuyan.wang@Sun.COM 	val = pci_config_get16(handle, ptr + PCIE_DEVCTL);
483410253Sxiuyan.wang@Sun.COM 	val = (val & ~PCIE_DEVCTL_MAX_READ_REQ_MASK) |
483510253Sxiuyan.wang@Sun.COM 	    PCIE_DEVCTL_MAX_READ_REQ_4096;
483610253Sxiuyan.wang@Sun.COM 	pci_config_put16(handle, ptr + PCIE_DEVCTL, val);
483710253Sxiuyan.wang@Sun.COM 	val = pci_config_get16(handle, ptr + PCIE_DEVCTL);
483810253Sxiuyan.wang@Sun.COM 	if ((val & (PCIE_DEVCTL_MAX_READ_REQ_4096)) !=
483910253Sxiuyan.wang@Sun.COM 	    PCIE_DEVCTL_MAX_READ_REQ_4096) {
484010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "could not set max read req (%x)\n", val);
484110253Sxiuyan.wang@Sun.COM 		return (EINVAL);
484210253Sxiuyan.wang@Sun.COM 	}
484310253Sxiuyan.wang@Sun.COM 	return (0);
484410253Sxiuyan.wang@Sun.COM }
484510253Sxiuyan.wang@Sun.COM 
484610253Sxiuyan.wang@Sun.COM static int
myri10ge_read_pcie_link_width(ddi_acc_handle_t handle,int * link)484710253Sxiuyan.wang@Sun.COM myri10ge_read_pcie_link_width(ddi_acc_handle_t handle, int *link)
484810253Sxiuyan.wang@Sun.COM {
484910253Sxiuyan.wang@Sun.COM 	int err;
485010253Sxiuyan.wang@Sun.COM 	uint16_t	val;
485110253Sxiuyan.wang@Sun.COM 	uint8_t		ptr;
485210253Sxiuyan.wang@Sun.COM 
485310253Sxiuyan.wang@Sun.COM 	err = myri10ge_find_cap(handle, &ptr, PCI_CAP_ID_PCI_E);
485410253Sxiuyan.wang@Sun.COM 	if (err != 0) {
485510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "could not set max read req\n");
485610253Sxiuyan.wang@Sun.COM 		return (ENXIO);
485710253Sxiuyan.wang@Sun.COM 	}
485810253Sxiuyan.wang@Sun.COM 
485910253Sxiuyan.wang@Sun.COM 	/* read link width */
486010253Sxiuyan.wang@Sun.COM 	val = pci_config_get16(handle, ptr + PCIE_LINKSTS);
486110253Sxiuyan.wang@Sun.COM 	val &= PCIE_LINKSTS_NEG_WIDTH_MASK;
486210253Sxiuyan.wang@Sun.COM 	*link = (val >> 4);
486310253Sxiuyan.wang@Sun.COM 	return (0);
486410253Sxiuyan.wang@Sun.COM }
486510253Sxiuyan.wang@Sun.COM 
486610253Sxiuyan.wang@Sun.COM static int
myri10ge_reset_nic(struct myri10ge_priv * mgp)486710253Sxiuyan.wang@Sun.COM myri10ge_reset_nic(struct myri10ge_priv *mgp)
486810253Sxiuyan.wang@Sun.COM {
486910253Sxiuyan.wang@Sun.COM 	ddi_acc_handle_t handle = mgp->cfg_hdl;
487010253Sxiuyan.wang@Sun.COM 	uint32_t reboot;
487110253Sxiuyan.wang@Sun.COM 	uint16_t cmd;
487210253Sxiuyan.wang@Sun.COM 	int err;
487310253Sxiuyan.wang@Sun.COM 
487410253Sxiuyan.wang@Sun.COM 	cmd = pci_config_get16(handle, PCI_CONF_COMM);
487510253Sxiuyan.wang@Sun.COM 	if ((cmd & PCI_COMM_ME) == 0) {
487610253Sxiuyan.wang@Sun.COM 		/*
487710253Sxiuyan.wang@Sun.COM 		 * Bus master DMA disabled?  Check to see if the card
487810253Sxiuyan.wang@Sun.COM 		 * rebooted due to a parity error For now, just report
487910253Sxiuyan.wang@Sun.COM 		 * it
488010253Sxiuyan.wang@Sun.COM 		 */
488110253Sxiuyan.wang@Sun.COM 
488210253Sxiuyan.wang@Sun.COM 		/* enter read32 mode */
488310253Sxiuyan.wang@Sun.COM 		pci_config_put8(handle, mgp->vso + 0x10, 0x3);
488410253Sxiuyan.wang@Sun.COM 		/* read REBOOT_STATUS (0xfffffff0) */
488510253Sxiuyan.wang@Sun.COM 		pci_config_put32(handle, mgp->vso + 0x18, 0xfffffff0);
488610253Sxiuyan.wang@Sun.COM 		reboot = pci_config_get16(handle, mgp->vso + 0x14);
488710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s NIC rebooted 0x%x\n", mgp->name, reboot);
488810253Sxiuyan.wang@Sun.COM 		return (0);
488910253Sxiuyan.wang@Sun.COM 	}
489010253Sxiuyan.wang@Sun.COM 	if (!myri10ge_watchdog_reset) {
489110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: not resetting\n", mgp->name);
489210253Sxiuyan.wang@Sun.COM 		return (1);
489310253Sxiuyan.wang@Sun.COM 	}
489410253Sxiuyan.wang@Sun.COM 
489510253Sxiuyan.wang@Sun.COM 	myri10ge_stop_locked(mgp);
489610253Sxiuyan.wang@Sun.COM 	err = myri10ge_start_locked(mgp);
489710253Sxiuyan.wang@Sun.COM 	if (err == DDI_FAILURE) {
489810253Sxiuyan.wang@Sun.COM 		return (0);
489910253Sxiuyan.wang@Sun.COM 	}
490010253Sxiuyan.wang@Sun.COM 	mac_tx_update(mgp->mh);
490110253Sxiuyan.wang@Sun.COM 	return (1);
490210253Sxiuyan.wang@Sun.COM }
490310253Sxiuyan.wang@Sun.COM 
490410253Sxiuyan.wang@Sun.COM static inline int
myri10ge_ring_stalled(myri10ge_tx_ring_t * tx)490510253Sxiuyan.wang@Sun.COM myri10ge_ring_stalled(myri10ge_tx_ring_t *tx)
490610253Sxiuyan.wang@Sun.COM {
490710253Sxiuyan.wang@Sun.COM 	if (tx->sched != tx->stall &&
490810253Sxiuyan.wang@Sun.COM 	    tx->done == tx->watchdog_done &&
490910253Sxiuyan.wang@Sun.COM 	    tx->watchdog_req != tx->watchdog_done)
491010253Sxiuyan.wang@Sun.COM 		return (1);
491110253Sxiuyan.wang@Sun.COM 	return (0);
491210253Sxiuyan.wang@Sun.COM }
491310253Sxiuyan.wang@Sun.COM 
491410253Sxiuyan.wang@Sun.COM static void
myri10ge_watchdog(void * arg)491510253Sxiuyan.wang@Sun.COM myri10ge_watchdog(void *arg)
491610253Sxiuyan.wang@Sun.COM {
491710253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
491810253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
491910253Sxiuyan.wang@Sun.COM 	myri10ge_tx_ring_t *tx;
492010253Sxiuyan.wang@Sun.COM 	int nic_ok = 1;
492110253Sxiuyan.wang@Sun.COM 	int slices_stalled, rx_pause, i;
492210253Sxiuyan.wang@Sun.COM 	int add_rx;
492310253Sxiuyan.wang@Sun.COM 
492410253Sxiuyan.wang@Sun.COM 	mgp = arg;
492510253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
492610253Sxiuyan.wang@Sun.COM 	if (mgp->running != MYRI10GE_ETH_RUNNING) {
492710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
492810253Sxiuyan.wang@Sun.COM 		    "%s not running, not rearming watchdog (%d)\n",
492910253Sxiuyan.wang@Sun.COM 		    mgp->name, mgp->running);
493010253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
493110253Sxiuyan.wang@Sun.COM 		return;
493210253Sxiuyan.wang@Sun.COM 	}
493310253Sxiuyan.wang@Sun.COM 
493410253Sxiuyan.wang@Sun.COM 	rx_pause = ntohl(mgp->ss[0].fw_stats->dropped_pause);
493510253Sxiuyan.wang@Sun.COM 
493610253Sxiuyan.wang@Sun.COM 	/*
493710253Sxiuyan.wang@Sun.COM 	 * make sure nic is stalled before we reset the nic, so as to
493810253Sxiuyan.wang@Sun.COM 	 * ensure we don't rip the transmit data structures out from
493910253Sxiuyan.wang@Sun.COM 	 * under a pending transmit
494010253Sxiuyan.wang@Sun.COM 	 */
494110253Sxiuyan.wang@Sun.COM 
494210253Sxiuyan.wang@Sun.COM 	for (slices_stalled = 0, i = 0; i < mgp->num_slices; i++) {
494310253Sxiuyan.wang@Sun.COM 		tx = &mgp->ss[i].tx;
494410253Sxiuyan.wang@Sun.COM 		slices_stalled = myri10ge_ring_stalled(tx);
494510253Sxiuyan.wang@Sun.COM 		if (slices_stalled)
494610253Sxiuyan.wang@Sun.COM 			break;
494710253Sxiuyan.wang@Sun.COM 	}
494810253Sxiuyan.wang@Sun.COM 
494910253Sxiuyan.wang@Sun.COM 	if (slices_stalled) {
495010253Sxiuyan.wang@Sun.COM 		if (mgp->watchdog_rx_pause == rx_pause) {
495110253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
495210253Sxiuyan.wang@Sun.COM 			    "%s slice %d stalled:(%d, %d, %d, %d, %d %d %d\n)",
495310253Sxiuyan.wang@Sun.COM 			    mgp->name, i, tx->sched, tx->stall,
495410253Sxiuyan.wang@Sun.COM 			    tx->done, tx->watchdog_done, tx->req, tx->pkt_done,
495510253Sxiuyan.wang@Sun.COM 			    (int)ntohl(mgp->ss[i].fw_stats->send_done_count));
495610253Sxiuyan.wang@Sun.COM 			nic_ok = myri10ge_reset_nic(mgp);
495710253Sxiuyan.wang@Sun.COM 		} else {
495810253Sxiuyan.wang@Sun.COM 			cmn_err(CE_WARN,
495910253Sxiuyan.wang@Sun.COM 			    "%s Flow controlled, check link partner\n",
496010253Sxiuyan.wang@Sun.COM 			    mgp->name);
496110253Sxiuyan.wang@Sun.COM 		}
496210253Sxiuyan.wang@Sun.COM 	}
496310253Sxiuyan.wang@Sun.COM 
496410253Sxiuyan.wang@Sun.COM 	if (!nic_ok) {
496510253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN,
496610253Sxiuyan.wang@Sun.COM 		    "%s Nic dead, not rearming watchdog\n", mgp->name);
496710253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
496810253Sxiuyan.wang@Sun.COM 		return;
496910253Sxiuyan.wang@Sun.COM 	}
497010253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
497110253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
497210253Sxiuyan.wang@Sun.COM 		tx = &ss->tx;
497310253Sxiuyan.wang@Sun.COM 		tx->watchdog_done = tx->done;
497410253Sxiuyan.wang@Sun.COM 		tx->watchdog_req = tx->req;
497510253Sxiuyan.wang@Sun.COM 		if (ss->watchdog_rx_copy != MYRI10GE_SLICE_STAT(rx_copy)) {
497610253Sxiuyan.wang@Sun.COM 			ss->watchdog_rx_copy = MYRI10GE_SLICE_STAT(rx_copy);
497710253Sxiuyan.wang@Sun.COM 			add_rx =
497810253Sxiuyan.wang@Sun.COM 			    min(ss->jpool.num_alloc,
497910253Sxiuyan.wang@Sun.COM 			    myri10ge_bigbufs_max -
498010253Sxiuyan.wang@Sun.COM 			    (ss->jpool.num_alloc -
498110253Sxiuyan.wang@Sun.COM 			    ss->jbufs_for_smalls));
498210253Sxiuyan.wang@Sun.COM 			if (add_rx != 0) {
498310253Sxiuyan.wang@Sun.COM 				(void) myri10ge_add_jbufs(ss, add_rx, 0);
498410253Sxiuyan.wang@Sun.COM 				/* now feed them to the firmware */
498510253Sxiuyan.wang@Sun.COM 				mutex_enter(&ss->jpool.mtx);
498610253Sxiuyan.wang@Sun.COM 				myri10ge_restock_jumbos(ss);
498710253Sxiuyan.wang@Sun.COM 				mutex_exit(&ss->jpool.mtx);
498810253Sxiuyan.wang@Sun.COM 			}
498910253Sxiuyan.wang@Sun.COM 		}
499010253Sxiuyan.wang@Sun.COM 	}
499110253Sxiuyan.wang@Sun.COM 	mgp->watchdog_rx_pause = rx_pause;
499210253Sxiuyan.wang@Sun.COM 
499310253Sxiuyan.wang@Sun.COM 	mgp->timer_id = timeout(myri10ge_watchdog, mgp,
499410253Sxiuyan.wang@Sun.COM 	    mgp->timer_ticks);
499510253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
499610253Sxiuyan.wang@Sun.COM }
499710253Sxiuyan.wang@Sun.COM 
499810253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
499910253Sxiuyan.wang@Sun.COM static int
myri10ge_get_coalesce(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * credp)500010253Sxiuyan.wang@Sun.COM myri10ge_get_coalesce(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp)
500110253Sxiuyan.wang@Sun.COM 
500210253Sxiuyan.wang@Sun.COM {
500310253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = (struct myri10ge_priv *)(void *)cp;
500410253Sxiuyan.wang@Sun.COM 	(void) mi_mpprintf(mp, "%d", mgp->intr_coal_delay);
500510253Sxiuyan.wang@Sun.COM 	return (0);
500610253Sxiuyan.wang@Sun.COM }
500710253Sxiuyan.wang@Sun.COM 
500810253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
500910253Sxiuyan.wang@Sun.COM static int
myri10ge_set_coalesce(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * credp)501010253Sxiuyan.wang@Sun.COM myri10ge_set_coalesce(queue_t *q, mblk_t *mp, char *value,
501110253Sxiuyan.wang@Sun.COM     caddr_t cp, cred_t *credp)
501210253Sxiuyan.wang@Sun.COM 
501310253Sxiuyan.wang@Sun.COM {
501410253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = (struct myri10ge_priv *)(void *)cp;
501510253Sxiuyan.wang@Sun.COM 	char *end;
501610253Sxiuyan.wang@Sun.COM 	size_t new_value;
501710253Sxiuyan.wang@Sun.COM 
501810253Sxiuyan.wang@Sun.COM 	new_value = mi_strtol(value, &end, 10);
501910253Sxiuyan.wang@Sun.COM 	if (end == value)
502010253Sxiuyan.wang@Sun.COM 		return (EINVAL);
502110253Sxiuyan.wang@Sun.COM 
502210253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
502310253Sxiuyan.wang@Sun.COM 	mgp->intr_coal_delay = (int)new_value;
502410253Sxiuyan.wang@Sun.COM 	*mgp->intr_coal_delay_ptr = htonl(mgp->intr_coal_delay);
502510253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
502610253Sxiuyan.wang@Sun.COM 	return (0);
502710253Sxiuyan.wang@Sun.COM }
502810253Sxiuyan.wang@Sun.COM 
502910253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
503010253Sxiuyan.wang@Sun.COM static int
myri10ge_get_pauseparam(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * credp)503110253Sxiuyan.wang@Sun.COM myri10ge_get_pauseparam(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp)
503210253Sxiuyan.wang@Sun.COM 
503310253Sxiuyan.wang@Sun.COM {
503410253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = (struct myri10ge_priv *)(void *)cp;
503510253Sxiuyan.wang@Sun.COM 	(void) mi_mpprintf(mp, "%d", mgp->pause);
503610253Sxiuyan.wang@Sun.COM 	return (0);
503710253Sxiuyan.wang@Sun.COM }
503810253Sxiuyan.wang@Sun.COM 
503910253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
504010253Sxiuyan.wang@Sun.COM static int
myri10ge_set_pauseparam(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * credp)504110253Sxiuyan.wang@Sun.COM myri10ge_set_pauseparam(queue_t *q, mblk_t *mp, char *value,
504210253Sxiuyan.wang@Sun.COM 			caddr_t cp, cred_t *credp)
504310253Sxiuyan.wang@Sun.COM 
504410253Sxiuyan.wang@Sun.COM {
504510253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = (struct myri10ge_priv *)(void *)cp;
504610253Sxiuyan.wang@Sun.COM 	char *end;
504710253Sxiuyan.wang@Sun.COM 	size_t new_value;
504810253Sxiuyan.wang@Sun.COM 	int err = 0;
504910253Sxiuyan.wang@Sun.COM 
505010253Sxiuyan.wang@Sun.COM 	new_value = mi_strtol(value, &end, 10);
505110253Sxiuyan.wang@Sun.COM 	if (end == value)
505210253Sxiuyan.wang@Sun.COM 		return (EINVAL);
505310253Sxiuyan.wang@Sun.COM 	if (new_value != 0)
505410253Sxiuyan.wang@Sun.COM 		new_value = 1;
505510253Sxiuyan.wang@Sun.COM 
505610253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
505710253Sxiuyan.wang@Sun.COM 	if (new_value != mgp->pause)
505810253Sxiuyan.wang@Sun.COM 		err = myri10ge_change_pause(mgp, new_value);
505910253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
506010253Sxiuyan.wang@Sun.COM 	return (err);
506110253Sxiuyan.wang@Sun.COM }
506210253Sxiuyan.wang@Sun.COM 
506310253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
506410253Sxiuyan.wang@Sun.COM static int
myri10ge_get_int(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * credp)506510253Sxiuyan.wang@Sun.COM myri10ge_get_int(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp)
506610253Sxiuyan.wang@Sun.COM 
506710253Sxiuyan.wang@Sun.COM {
506810253Sxiuyan.wang@Sun.COM 	(void) mi_mpprintf(mp, "%d", *(int *)(void *)cp);
506910253Sxiuyan.wang@Sun.COM 	return (0);
507010253Sxiuyan.wang@Sun.COM }
507110253Sxiuyan.wang@Sun.COM 
507210253Sxiuyan.wang@Sun.COM /*ARGSUSED*/
507310253Sxiuyan.wang@Sun.COM static int
myri10ge_set_int(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * credp)507410253Sxiuyan.wang@Sun.COM myri10ge_set_int(queue_t *q, mblk_t *mp, char *value,
507510253Sxiuyan.wang@Sun.COM     caddr_t cp, cred_t *credp)
507610253Sxiuyan.wang@Sun.COM 
507710253Sxiuyan.wang@Sun.COM {
507810253Sxiuyan.wang@Sun.COM 	char *end;
507910253Sxiuyan.wang@Sun.COM 	size_t new_value;
508010253Sxiuyan.wang@Sun.COM 
508110253Sxiuyan.wang@Sun.COM 	new_value = mi_strtol(value, &end, 10);
508210253Sxiuyan.wang@Sun.COM 	if (end == value)
508310253Sxiuyan.wang@Sun.COM 		return (EINVAL);
508410253Sxiuyan.wang@Sun.COM 	*(int *)(void *)cp = new_value;
508510253Sxiuyan.wang@Sun.COM 
508610253Sxiuyan.wang@Sun.COM 	return (0);
508710253Sxiuyan.wang@Sun.COM }
508810253Sxiuyan.wang@Sun.COM 
508910253Sxiuyan.wang@Sun.COM static void
myri10ge_ndd_init(struct myri10ge_priv * mgp)509010253Sxiuyan.wang@Sun.COM myri10ge_ndd_init(struct myri10ge_priv *mgp)
509110253Sxiuyan.wang@Sun.COM {
509210253Sxiuyan.wang@Sun.COM 	mgp->nd_head = NULL;
509310253Sxiuyan.wang@Sun.COM 
509410253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_intr_coal_delay",
509510253Sxiuyan.wang@Sun.COM 	    myri10ge_get_coalesce, myri10ge_set_coalesce, (caddr_t)mgp);
509610253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_flow_control",
509710253Sxiuyan.wang@Sun.COM 	    myri10ge_get_pauseparam, myri10ge_set_pauseparam, (caddr_t)mgp);
509810253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_verbose",
509910253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int, (caddr_t)&myri10ge_verbose);
510010253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_deassert_wait",
510110253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
510210253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_deassert_wait);
510310253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_bigbufs_max",
510410253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
510510253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_bigbufs_max);
510610253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_lro",
510710253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
510810253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_lro);
510910253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_lro_max_aggr",
511010253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
511110253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_lro_max_aggr);
511210253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_tx_hash",
511310253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
511410253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_tx_hash);
511510253Sxiuyan.wang@Sun.COM 	(void) nd_load(&mgp->nd_head, "myri10ge_lso_copy",
511610253Sxiuyan.wang@Sun.COM 	    myri10ge_get_int, myri10ge_set_int,
511710253Sxiuyan.wang@Sun.COM 	    (caddr_t)&myri10ge_lso_copy);
511810253Sxiuyan.wang@Sun.COM }
511910253Sxiuyan.wang@Sun.COM 
512010253Sxiuyan.wang@Sun.COM static void
myri10ge_ndd_fini(struct myri10ge_priv * mgp)512110253Sxiuyan.wang@Sun.COM myri10ge_ndd_fini(struct myri10ge_priv *mgp)
512210253Sxiuyan.wang@Sun.COM {
512310253Sxiuyan.wang@Sun.COM 	nd_free(&mgp->nd_head);
512410253Sxiuyan.wang@Sun.COM }
512510253Sxiuyan.wang@Sun.COM 
512610253Sxiuyan.wang@Sun.COM static void
myri10ge_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)512710253Sxiuyan.wang@Sun.COM myri10ge_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
512810253Sxiuyan.wang@Sun.COM {
512910253Sxiuyan.wang@Sun.COM 	struct iocblk *iocp;
513010253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
513110253Sxiuyan.wang@Sun.COM 	int cmd, ok, err;
513210253Sxiuyan.wang@Sun.COM 
513310253Sxiuyan.wang@Sun.COM 	iocp = (struct iocblk *)(void *)mp->b_rptr;
513410253Sxiuyan.wang@Sun.COM 	cmd = iocp->ioc_cmd;
513510253Sxiuyan.wang@Sun.COM 
513610253Sxiuyan.wang@Sun.COM 	ok = 0;
513710253Sxiuyan.wang@Sun.COM 	err = 0;
513810253Sxiuyan.wang@Sun.COM 
513910253Sxiuyan.wang@Sun.COM 	switch (cmd) {
514010253Sxiuyan.wang@Sun.COM 	case ND_GET:
514110253Sxiuyan.wang@Sun.COM 	case ND_SET:
514210253Sxiuyan.wang@Sun.COM 		ok = nd_getset(wq, mgp->nd_head, mp);
514310253Sxiuyan.wang@Sun.COM 		break;
514410253Sxiuyan.wang@Sun.COM 	default:
514510253Sxiuyan.wang@Sun.COM 		break;
514610253Sxiuyan.wang@Sun.COM 	}
514710253Sxiuyan.wang@Sun.COM 	if (!ok)
514810253Sxiuyan.wang@Sun.COM 		err = EINVAL;
514910253Sxiuyan.wang@Sun.COM 	else
515010253Sxiuyan.wang@Sun.COM 		err = iocp->ioc_error;
515110253Sxiuyan.wang@Sun.COM 
515210253Sxiuyan.wang@Sun.COM 	if (!err)
515310253Sxiuyan.wang@Sun.COM 		miocack(wq, mp, iocp->ioc_count, err);
515410253Sxiuyan.wang@Sun.COM 	else
515510253Sxiuyan.wang@Sun.COM 		miocnak(wq, mp, 0, err);
515610253Sxiuyan.wang@Sun.COM }
515710253Sxiuyan.wang@Sun.COM 
515810253Sxiuyan.wang@Sun.COM static struct myri10ge_priv *mgp_list;
515910253Sxiuyan.wang@Sun.COM 
516010253Sxiuyan.wang@Sun.COM struct myri10ge_priv *
myri10ge_get_instance(uint_t unit)516110253Sxiuyan.wang@Sun.COM myri10ge_get_instance(uint_t unit)
516210253Sxiuyan.wang@Sun.COM {
516310253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
516410253Sxiuyan.wang@Sun.COM 
516510253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
516610253Sxiuyan.wang@Sun.COM 	for (mgp = mgp_list; mgp != NULL; mgp = mgp->next) {
516710253Sxiuyan.wang@Sun.COM 		if (unit == ddi_get_instance(mgp->dip)) {
516810253Sxiuyan.wang@Sun.COM 			mgp->refcnt++;
516910253Sxiuyan.wang@Sun.COM 			break;
517010253Sxiuyan.wang@Sun.COM 		}
517110253Sxiuyan.wang@Sun.COM 	}
517210253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
517310253Sxiuyan.wang@Sun.COM 	return (mgp);
517410253Sxiuyan.wang@Sun.COM }
517510253Sxiuyan.wang@Sun.COM 
517610253Sxiuyan.wang@Sun.COM void
myri10ge_put_instance(struct myri10ge_priv * mgp)517710253Sxiuyan.wang@Sun.COM myri10ge_put_instance(struct myri10ge_priv *mgp)
517810253Sxiuyan.wang@Sun.COM {
517910253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
518010253Sxiuyan.wang@Sun.COM 	mgp->refcnt--;
518110253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
518210253Sxiuyan.wang@Sun.COM }
518310253Sxiuyan.wang@Sun.COM 
518410253Sxiuyan.wang@Sun.COM static boolean_t
myri10ge_m_getcapab(void * arg,mac_capab_t cap,void * cap_data)518510253Sxiuyan.wang@Sun.COM myri10ge_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
518610253Sxiuyan.wang@Sun.COM {
518710253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
518810253Sxiuyan.wang@Sun.COM 	uint32_t *cap_hcksum;
518910253Sxiuyan.wang@Sun.COM 	mac_capab_lso_t *cap_lso;
519010253Sxiuyan.wang@Sun.COM 	mac_capab_rings_t *cap_rings;
519110253Sxiuyan.wang@Sun.COM 
519210253Sxiuyan.wang@Sun.COM 	switch (cap) {
519310253Sxiuyan.wang@Sun.COM 	case MAC_CAPAB_HCKSUM:
519410253Sxiuyan.wang@Sun.COM 		cap_hcksum = cap_data;
519510253Sxiuyan.wang@Sun.COM 		*cap_hcksum = HCKSUM_INET_PARTIAL;
519610253Sxiuyan.wang@Sun.COM 		break;
519710253Sxiuyan.wang@Sun.COM 	case MAC_CAPAB_RINGS:
519810253Sxiuyan.wang@Sun.COM 		cap_rings = cap_data;
519910253Sxiuyan.wang@Sun.COM 		switch (cap_rings->mr_type) {
520010253Sxiuyan.wang@Sun.COM 		case MAC_RING_TYPE_RX:
520110253Sxiuyan.wang@Sun.COM 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
520210253Sxiuyan.wang@Sun.COM 			cap_rings->mr_rnum = mgp->num_slices;
520310253Sxiuyan.wang@Sun.COM 			cap_rings->mr_gnum = 1;
520410253Sxiuyan.wang@Sun.COM 			cap_rings->mr_rget = myri10ge_fill_ring;
520510253Sxiuyan.wang@Sun.COM 			cap_rings->mr_gget = myri10ge_fill_group;
520610253Sxiuyan.wang@Sun.COM 			break;
520710253Sxiuyan.wang@Sun.COM 		case MAC_RING_TYPE_TX:
520810253Sxiuyan.wang@Sun.COM 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
520910253Sxiuyan.wang@Sun.COM 			cap_rings->mr_rnum = mgp->num_slices;
521010253Sxiuyan.wang@Sun.COM 			cap_rings->mr_gnum = 0;
521110253Sxiuyan.wang@Sun.COM 			cap_rings->mr_rget = myri10ge_fill_ring;
521210253Sxiuyan.wang@Sun.COM 			cap_rings->mr_gget = NULL;
521310253Sxiuyan.wang@Sun.COM 			break;
521410253Sxiuyan.wang@Sun.COM 		default:
521510253Sxiuyan.wang@Sun.COM 			return (B_FALSE);
521610253Sxiuyan.wang@Sun.COM 		}
521710253Sxiuyan.wang@Sun.COM 		break;
521810253Sxiuyan.wang@Sun.COM 	case MAC_CAPAB_LSO:
521910253Sxiuyan.wang@Sun.COM 		cap_lso = cap_data;
522010253Sxiuyan.wang@Sun.COM 		if (!myri10ge_use_lso)
522110253Sxiuyan.wang@Sun.COM 			return (B_FALSE);
522210253Sxiuyan.wang@Sun.COM 		if (!(mgp->features & MYRI10GE_TSO))
522310253Sxiuyan.wang@Sun.COM 			return (B_FALSE);
522410253Sxiuyan.wang@Sun.COM 		cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
522510253Sxiuyan.wang@Sun.COM 		cap_lso->lso_basic_tcp_ipv4.lso_max = (uint16_t)-1;
522610253Sxiuyan.wang@Sun.COM 		break;
522710253Sxiuyan.wang@Sun.COM 
522810253Sxiuyan.wang@Sun.COM 	default:
522910253Sxiuyan.wang@Sun.COM 		return (B_FALSE);
523010253Sxiuyan.wang@Sun.COM 	}
523110253Sxiuyan.wang@Sun.COM 	return (B_TRUE);
523210253Sxiuyan.wang@Sun.COM }
523310253Sxiuyan.wang@Sun.COM 
523410253Sxiuyan.wang@Sun.COM 
523510253Sxiuyan.wang@Sun.COM static int
myri10ge_m_stat(void * arg,uint_t stat,uint64_t * val)523610253Sxiuyan.wang@Sun.COM myri10ge_m_stat(void *arg, uint_t stat, uint64_t *val)
523710253Sxiuyan.wang@Sun.COM {
523810253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = arg;
523910253Sxiuyan.wang@Sun.COM 	struct myri10ge_rx_ring_stats *rstat;
524010253Sxiuyan.wang@Sun.COM 	struct myri10ge_tx_ring_stats *tstat;
524110253Sxiuyan.wang@Sun.COM 	mcp_irq_data_t *fw_stats = mgp->ss[0].fw_stats;
524210253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
524310253Sxiuyan.wang@Sun.COM 	uint64_t tmp = 0;
524410253Sxiuyan.wang@Sun.COM 	int i;
524510253Sxiuyan.wang@Sun.COM 
524610253Sxiuyan.wang@Sun.COM 	switch (stat) {
524710253Sxiuyan.wang@Sun.COM 	case MAC_STAT_IFSPEED:
524810253Sxiuyan.wang@Sun.COM 		*val = 10ull * 1000ull * 1000000ull;
524910253Sxiuyan.wang@Sun.COM 		break;
525010253Sxiuyan.wang@Sun.COM 
525110253Sxiuyan.wang@Sun.COM 	case MAC_STAT_MULTIRCV:
525210253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
525310253Sxiuyan.wang@Sun.COM 			rstat = &mgp->ss[i].rx_stats;
525410253Sxiuyan.wang@Sun.COM 			tmp += rstat->multircv;
525510253Sxiuyan.wang@Sun.COM 		}
525610253Sxiuyan.wang@Sun.COM 		*val = tmp;
525710253Sxiuyan.wang@Sun.COM 		break;
525810253Sxiuyan.wang@Sun.COM 
525910253Sxiuyan.wang@Sun.COM 	case MAC_STAT_BRDCSTRCV:
526010253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
526110253Sxiuyan.wang@Sun.COM 			rstat = &mgp->ss[i].rx_stats;
526210253Sxiuyan.wang@Sun.COM 			tmp += rstat->brdcstrcv;
526310253Sxiuyan.wang@Sun.COM 		}
526410253Sxiuyan.wang@Sun.COM 		*val = tmp;
526510253Sxiuyan.wang@Sun.COM 		break;
526610253Sxiuyan.wang@Sun.COM 
526710253Sxiuyan.wang@Sun.COM 	case MAC_STAT_MULTIXMT:
526810253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
526910253Sxiuyan.wang@Sun.COM 			tstat = &mgp->ss[i].tx.stats;
527010253Sxiuyan.wang@Sun.COM 			tmp += tstat->multixmt;
527110253Sxiuyan.wang@Sun.COM 		}
527210253Sxiuyan.wang@Sun.COM 		*val = tmp;
527310253Sxiuyan.wang@Sun.COM 		break;
527410253Sxiuyan.wang@Sun.COM 
527510253Sxiuyan.wang@Sun.COM 	case MAC_STAT_BRDCSTXMT:
527610253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
527710253Sxiuyan.wang@Sun.COM 			tstat = &mgp->ss[i].tx.stats;
527810253Sxiuyan.wang@Sun.COM 			tmp += tstat->brdcstxmt;
527910253Sxiuyan.wang@Sun.COM 		}
528010253Sxiuyan.wang@Sun.COM 		*val = tmp;
528110253Sxiuyan.wang@Sun.COM 		break;
528210253Sxiuyan.wang@Sun.COM 
528310253Sxiuyan.wang@Sun.COM 	case MAC_STAT_NORCVBUF:
528410253Sxiuyan.wang@Sun.COM 		tmp = ntohl(fw_stats->dropped_no_big_buffer);
528510253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_no_small_buffer);
528610253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_link_overflow);
528710253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
528810253Sxiuyan.wang@Sun.COM 			ss = &mgp->ss[i];
528910253Sxiuyan.wang@Sun.COM 			tmp += MYRI10GE_SLICE_STAT(rx_big_nobuf);
529010253Sxiuyan.wang@Sun.COM 			tmp += MYRI10GE_SLICE_STAT(rx_small_nobuf);
529110253Sxiuyan.wang@Sun.COM 		}
529210253Sxiuyan.wang@Sun.COM 		*val = tmp;
529310253Sxiuyan.wang@Sun.COM 		break;
529410253Sxiuyan.wang@Sun.COM 
529510253Sxiuyan.wang@Sun.COM 	case MAC_STAT_IERRORS:
529610253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_bad_crc32);
529710253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_bad_phy);
529810253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_runt);
529910253Sxiuyan.wang@Sun.COM 		tmp += ntohl(fw_stats->dropped_overrun);
530010253Sxiuyan.wang@Sun.COM 		*val = tmp;
530110253Sxiuyan.wang@Sun.COM 		break;
530210253Sxiuyan.wang@Sun.COM 
530310253Sxiuyan.wang@Sun.COM 	case MAC_STAT_OERRORS:
530410253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
530510253Sxiuyan.wang@Sun.COM 			ss = &mgp->ss[i];
530610253Sxiuyan.wang@Sun.COM 			tmp += MYRI10GE_SLICE_STAT(xmit_lsobadflags);
530710253Sxiuyan.wang@Sun.COM 			tmp += MYRI10GE_SLICE_STAT(xmit_err);
530810253Sxiuyan.wang@Sun.COM 		}
530910253Sxiuyan.wang@Sun.COM 		*val = tmp;
531010253Sxiuyan.wang@Sun.COM 		break;
531110253Sxiuyan.wang@Sun.COM 
531210253Sxiuyan.wang@Sun.COM 	case MAC_STAT_RBYTES:
531310253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
531410253Sxiuyan.wang@Sun.COM 			rstat = &mgp->ss[i].rx_stats;
531510253Sxiuyan.wang@Sun.COM 			tmp += rstat->ibytes;
531610253Sxiuyan.wang@Sun.COM 		}
531710253Sxiuyan.wang@Sun.COM 		*val = tmp;
531810253Sxiuyan.wang@Sun.COM 		break;
531910253Sxiuyan.wang@Sun.COM 
532010253Sxiuyan.wang@Sun.COM 	case MAC_STAT_IPACKETS:
532110253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
532210253Sxiuyan.wang@Sun.COM 			rstat = &mgp->ss[i].rx_stats;
532310253Sxiuyan.wang@Sun.COM 			tmp += rstat->ipackets;
532410253Sxiuyan.wang@Sun.COM 		}
532510253Sxiuyan.wang@Sun.COM 		*val = tmp;
532610253Sxiuyan.wang@Sun.COM 		break;
532710253Sxiuyan.wang@Sun.COM 
532810253Sxiuyan.wang@Sun.COM 	case MAC_STAT_OBYTES:
532910253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
533010253Sxiuyan.wang@Sun.COM 			tstat = &mgp->ss[i].tx.stats;
533110253Sxiuyan.wang@Sun.COM 			tmp += tstat->obytes;
533210253Sxiuyan.wang@Sun.COM 		}
533310253Sxiuyan.wang@Sun.COM 		*val = tmp;
533410253Sxiuyan.wang@Sun.COM 		break;
533510253Sxiuyan.wang@Sun.COM 
533610253Sxiuyan.wang@Sun.COM 	case MAC_STAT_OPACKETS:
533710253Sxiuyan.wang@Sun.COM 		for (i = 0; i < mgp->num_slices; i++) {
533810253Sxiuyan.wang@Sun.COM 			tstat = &mgp->ss[i].tx.stats;
533910253Sxiuyan.wang@Sun.COM 			tmp += tstat->opackets;
534010253Sxiuyan.wang@Sun.COM 		}
534110253Sxiuyan.wang@Sun.COM 		*val = tmp;
534210253Sxiuyan.wang@Sun.COM 		break;
534310253Sxiuyan.wang@Sun.COM 
534410253Sxiuyan.wang@Sun.COM 	case ETHER_STAT_TOOLONG_ERRORS:
534510253Sxiuyan.wang@Sun.COM 		*val = ntohl(fw_stats->dropped_overrun);
534610253Sxiuyan.wang@Sun.COM 		break;
534710253Sxiuyan.wang@Sun.COM 
534810253Sxiuyan.wang@Sun.COM #ifdef SOLARIS_S11
534910253Sxiuyan.wang@Sun.COM 	case ETHER_STAT_TOOSHORT_ERRORS:
535010253Sxiuyan.wang@Sun.COM 		*val = ntohl(fw_stats->dropped_runt);
535110253Sxiuyan.wang@Sun.COM 		break;
535210253Sxiuyan.wang@Sun.COM #endif
535310253Sxiuyan.wang@Sun.COM 
535410253Sxiuyan.wang@Sun.COM 	case ETHER_STAT_LINK_PAUSE:
535510253Sxiuyan.wang@Sun.COM 		*val = mgp->pause;
535610253Sxiuyan.wang@Sun.COM 		break;
535710253Sxiuyan.wang@Sun.COM 
535810253Sxiuyan.wang@Sun.COM 	case ETHER_STAT_LINK_AUTONEG:
535910253Sxiuyan.wang@Sun.COM 		*val = 1;
536010253Sxiuyan.wang@Sun.COM 		break;
536110253Sxiuyan.wang@Sun.COM 
536210253Sxiuyan.wang@Sun.COM 	case ETHER_STAT_LINK_DUPLEX:
536310253Sxiuyan.wang@Sun.COM 		*val = LINK_DUPLEX_FULL;
536410253Sxiuyan.wang@Sun.COM 		break;
536510253Sxiuyan.wang@Sun.COM 
536610253Sxiuyan.wang@Sun.COM 	default:
536710253Sxiuyan.wang@Sun.COM 		return (ENOTSUP);
536810253Sxiuyan.wang@Sun.COM 	}
536910253Sxiuyan.wang@Sun.COM 
537010253Sxiuyan.wang@Sun.COM 	return (0);
537110253Sxiuyan.wang@Sun.COM }
537210253Sxiuyan.wang@Sun.COM 
537310253Sxiuyan.wang@Sun.COM static mac_callbacks_t myri10ge_m_callbacks = {
537410253Sxiuyan.wang@Sun.COM 	(MC_IOCTL | MC_GETCAPAB),
537510253Sxiuyan.wang@Sun.COM 	myri10ge_m_stat,
537610253Sxiuyan.wang@Sun.COM 	myri10ge_m_start,
537710253Sxiuyan.wang@Sun.COM 	myri10ge_m_stop,
537810253Sxiuyan.wang@Sun.COM 	myri10ge_m_promisc,
537910253Sxiuyan.wang@Sun.COM 	myri10ge_m_multicst,
538010253Sxiuyan.wang@Sun.COM 	NULL,
538110253Sxiuyan.wang@Sun.COM 	NULL,
5382*11878SVenu.Iyer@Sun.COM 	NULL,
538310253Sxiuyan.wang@Sun.COM 	myri10ge_m_ioctl,
538410253Sxiuyan.wang@Sun.COM 	myri10ge_m_getcapab
538510253Sxiuyan.wang@Sun.COM };
538610253Sxiuyan.wang@Sun.COM 
538710253Sxiuyan.wang@Sun.COM 
538810253Sxiuyan.wang@Sun.COM static int
myri10ge_probe_slices(struct myri10ge_priv * mgp)538910253Sxiuyan.wang@Sun.COM myri10ge_probe_slices(struct myri10ge_priv *mgp)
539010253Sxiuyan.wang@Sun.COM {
539110253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
539210253Sxiuyan.wang@Sun.COM 	int status;
539310253Sxiuyan.wang@Sun.COM 
539410253Sxiuyan.wang@Sun.COM 	mgp->num_slices = 1;
539510253Sxiuyan.wang@Sun.COM 
539610253Sxiuyan.wang@Sun.COM 	/* hit the board with a reset to ensure it is alive */
539710253Sxiuyan.wang@Sun.COM 	(void) memset(&cmd, 0, sizeof (cmd));
539810253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd);
539910253Sxiuyan.wang@Sun.COM 	if (status != 0) {
540010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed reset\n", mgp->name);
540110253Sxiuyan.wang@Sun.COM 		return (ENXIO);
540210253Sxiuyan.wang@Sun.COM 	}
540310253Sxiuyan.wang@Sun.COM 
540410253Sxiuyan.wang@Sun.COM 	if (myri10ge_use_msix == 0)
540510253Sxiuyan.wang@Sun.COM 		return (0);
540610253Sxiuyan.wang@Sun.COM 
540710253Sxiuyan.wang@Sun.COM 	/* tell it the size of the interrupt queues */
540810253Sxiuyan.wang@Sun.COM 	cmd.data0 = mgp->max_intr_slots * sizeof (struct mcp_slot);
540910253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
541010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
541110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed MXGEFW_CMD_SET_INTRQ_SIZE\n",
541210253Sxiuyan.wang@Sun.COM 		    mgp->name);
541310253Sxiuyan.wang@Sun.COM 		return (ENXIO);
541410253Sxiuyan.wang@Sun.COM 	}
541510253Sxiuyan.wang@Sun.COM 
541610253Sxiuyan.wang@Sun.COM 	/* ask the maximum number of slices it supports */
541710253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
541810253Sxiuyan.wang@Sun.COM 	    &cmd);
541910253Sxiuyan.wang@Sun.COM 	if (status != 0)
542010253Sxiuyan.wang@Sun.COM 		return (0);
542110253Sxiuyan.wang@Sun.COM 
542210253Sxiuyan.wang@Sun.COM 	mgp->num_slices = cmd.data0;
542310253Sxiuyan.wang@Sun.COM 
542410253Sxiuyan.wang@Sun.COM 	/*
542510253Sxiuyan.wang@Sun.COM 	 * if the admin did not specify a limit to how many
542610253Sxiuyan.wang@Sun.COM 	 * slices we should use, cap it automatically to the
542710253Sxiuyan.wang@Sun.COM 	 * number of CPUs currently online
542810253Sxiuyan.wang@Sun.COM 	 */
542910253Sxiuyan.wang@Sun.COM 	if (myri10ge_max_slices == -1)
543010253Sxiuyan.wang@Sun.COM 		myri10ge_max_slices = ncpus;
543110253Sxiuyan.wang@Sun.COM 
543210253Sxiuyan.wang@Sun.COM 	if (mgp->num_slices > myri10ge_max_slices)
543310253Sxiuyan.wang@Sun.COM 		mgp->num_slices = myri10ge_max_slices;
543410253Sxiuyan.wang@Sun.COM 
543510253Sxiuyan.wang@Sun.COM 
543610253Sxiuyan.wang@Sun.COM 	/*
543710253Sxiuyan.wang@Sun.COM 	 * Now try to allocate as many MSI-X vectors as we have
543810253Sxiuyan.wang@Sun.COM 	 * slices. We give up on MSI-X if we can only get a single
543910253Sxiuyan.wang@Sun.COM 	 * vector.
544010253Sxiuyan.wang@Sun.COM 	 */
544110253Sxiuyan.wang@Sun.COM 	while (mgp->num_slices > 1) {
544210253Sxiuyan.wang@Sun.COM 		/* make sure it is a power of two */
544310253Sxiuyan.wang@Sun.COM 		while (mgp->num_slices & (mgp->num_slices - 1))
544410253Sxiuyan.wang@Sun.COM 			mgp->num_slices--;
544510253Sxiuyan.wang@Sun.COM 		if (mgp->num_slices == 1)
544610253Sxiuyan.wang@Sun.COM 			return (0);
544710253Sxiuyan.wang@Sun.COM 
544810253Sxiuyan.wang@Sun.COM 		status = myri10ge_add_intrs(mgp, 0);
544910253Sxiuyan.wang@Sun.COM 		if (status == 0) {
545010253Sxiuyan.wang@Sun.COM 			myri10ge_rem_intrs(mgp, 0);
545110253Sxiuyan.wang@Sun.COM 			if (mgp->intr_cnt == mgp->num_slices) {
545210253Sxiuyan.wang@Sun.COM 				if (myri10ge_verbose)
545310253Sxiuyan.wang@Sun.COM 					printf("Got %d slices!\n",
545410253Sxiuyan.wang@Sun.COM 					    mgp->num_slices);
545510253Sxiuyan.wang@Sun.COM 				return (0);
545610253Sxiuyan.wang@Sun.COM 			}
545710253Sxiuyan.wang@Sun.COM 			mgp->num_slices = mgp->intr_cnt;
545810253Sxiuyan.wang@Sun.COM 		} else {
545910253Sxiuyan.wang@Sun.COM 			mgp->num_slices = mgp->num_slices / 2;
546010253Sxiuyan.wang@Sun.COM 		}
546110253Sxiuyan.wang@Sun.COM 	}
546210253Sxiuyan.wang@Sun.COM 
546310253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
546410253Sxiuyan.wang@Sun.COM 		printf("Got %d slices\n", mgp->num_slices);
546510253Sxiuyan.wang@Sun.COM 	return (0);
546610253Sxiuyan.wang@Sun.COM }
546710253Sxiuyan.wang@Sun.COM 
546810253Sxiuyan.wang@Sun.COM static void
myri10ge_lro_free(struct myri10ge_slice_state * ss)546910253Sxiuyan.wang@Sun.COM myri10ge_lro_free(struct myri10ge_slice_state *ss)
547010253Sxiuyan.wang@Sun.COM {
547110253Sxiuyan.wang@Sun.COM 	struct lro_entry *lro;
547210253Sxiuyan.wang@Sun.COM 
547310253Sxiuyan.wang@Sun.COM 	while (ss->lro_free != NULL) {
547410253Sxiuyan.wang@Sun.COM 		lro = ss->lro_free;
547510253Sxiuyan.wang@Sun.COM 		ss->lro_free = lro->next;
547610253Sxiuyan.wang@Sun.COM 		kmem_free(lro, sizeof (*lro));
547710253Sxiuyan.wang@Sun.COM 	}
547810253Sxiuyan.wang@Sun.COM }
547910253Sxiuyan.wang@Sun.COM 
548010253Sxiuyan.wang@Sun.COM static void
myri10ge_lro_alloc(struct myri10ge_slice_state * ss)548110253Sxiuyan.wang@Sun.COM myri10ge_lro_alloc(struct myri10ge_slice_state *ss)
548210253Sxiuyan.wang@Sun.COM {
548310253Sxiuyan.wang@Sun.COM 	struct lro_entry *lro;
548410253Sxiuyan.wang@Sun.COM 	int idx;
548510253Sxiuyan.wang@Sun.COM 
548610253Sxiuyan.wang@Sun.COM 	ss->lro_free = NULL;
548710253Sxiuyan.wang@Sun.COM 	ss->lro_active = NULL;
548810253Sxiuyan.wang@Sun.COM 
548910253Sxiuyan.wang@Sun.COM 	for (idx = 0; idx < myri10ge_lro_cnt; idx++) {
549010253Sxiuyan.wang@Sun.COM 		lro = kmem_zalloc(sizeof (*lro), KM_SLEEP);
549110253Sxiuyan.wang@Sun.COM 		if (lro == NULL)
549210253Sxiuyan.wang@Sun.COM 			continue;
549310253Sxiuyan.wang@Sun.COM 		lro->next = ss->lro_free;
549410253Sxiuyan.wang@Sun.COM 		ss->lro_free = lro;
549510253Sxiuyan.wang@Sun.COM 	}
549610253Sxiuyan.wang@Sun.COM }
549710253Sxiuyan.wang@Sun.COM 
549810253Sxiuyan.wang@Sun.COM static void
myri10ge_free_slices(struct myri10ge_priv * mgp)549910253Sxiuyan.wang@Sun.COM myri10ge_free_slices(struct myri10ge_priv *mgp)
550010253Sxiuyan.wang@Sun.COM {
550110253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
550210253Sxiuyan.wang@Sun.COM 	size_t bytes;
550310253Sxiuyan.wang@Sun.COM 	int i;
550410253Sxiuyan.wang@Sun.COM 
550510253Sxiuyan.wang@Sun.COM 	if (mgp->ss == NULL)
550610253Sxiuyan.wang@Sun.COM 		return;
550710253Sxiuyan.wang@Sun.COM 
550810253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
550910253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
551010253Sxiuyan.wang@Sun.COM 		if (ss->rx_done.entry == NULL)
551110253Sxiuyan.wang@Sun.COM 			continue;
551210253Sxiuyan.wang@Sun.COM 		myri10ge_dma_free(&ss->rx_done.dma);
551310253Sxiuyan.wang@Sun.COM 		ss->rx_done.entry = NULL;
551410253Sxiuyan.wang@Sun.COM 		if (ss->fw_stats == NULL)
551510253Sxiuyan.wang@Sun.COM 			continue;
551610253Sxiuyan.wang@Sun.COM 		myri10ge_dma_free(&ss->fw_stats_dma);
551710253Sxiuyan.wang@Sun.COM 		ss->fw_stats = NULL;
551810253Sxiuyan.wang@Sun.COM 		mutex_destroy(&ss->rx_lock);
551910253Sxiuyan.wang@Sun.COM 		mutex_destroy(&ss->tx.lock);
552010253Sxiuyan.wang@Sun.COM 		mutex_destroy(&ss->tx.handle_lock);
552110253Sxiuyan.wang@Sun.COM 		mutex_destroy(&ss->poll_lock);
552210253Sxiuyan.wang@Sun.COM 		myri10ge_jpool_fini(ss);
552310253Sxiuyan.wang@Sun.COM 		myri10ge_slice_stat_destroy(ss);
552410253Sxiuyan.wang@Sun.COM 		myri10ge_lro_free(ss);
552510253Sxiuyan.wang@Sun.COM 	}
552610253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*mgp->ss) * mgp->num_slices;
552710253Sxiuyan.wang@Sun.COM 	kmem_free(mgp->ss, bytes);
552810253Sxiuyan.wang@Sun.COM 	mgp->ss = NULL;
552910253Sxiuyan.wang@Sun.COM }
553010253Sxiuyan.wang@Sun.COM 
553110253Sxiuyan.wang@Sun.COM 
553210253Sxiuyan.wang@Sun.COM static int
myri10ge_alloc_slices(struct myri10ge_priv * mgp)553310253Sxiuyan.wang@Sun.COM myri10ge_alloc_slices(struct myri10ge_priv *mgp)
553410253Sxiuyan.wang@Sun.COM {
553510253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss;
553610253Sxiuyan.wang@Sun.COM 	size_t bytes;
553710253Sxiuyan.wang@Sun.COM 	int i;
553810253Sxiuyan.wang@Sun.COM 
553910253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*mgp->ss) * mgp->num_slices;
554010253Sxiuyan.wang@Sun.COM 	mgp->ss = kmem_zalloc(bytes, KM_SLEEP);
554110253Sxiuyan.wang@Sun.COM 	if (mgp->ss == NULL)
554210253Sxiuyan.wang@Sun.COM 		return (ENOMEM);
554310253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
554410253Sxiuyan.wang@Sun.COM 		ss = &mgp->ss[i];
554510253Sxiuyan.wang@Sun.COM 
554610253Sxiuyan.wang@Sun.COM 		ss->mgp = mgp;
554710253Sxiuyan.wang@Sun.COM 
554810253Sxiuyan.wang@Sun.COM 		/* allocate the per-slice firmware stats */
554910253Sxiuyan.wang@Sun.COM 		bytes = sizeof (*ss->fw_stats);
555010253Sxiuyan.wang@Sun.COM 		ss->fw_stats = (mcp_irq_data_t *)(void *)
555110253Sxiuyan.wang@Sun.COM 		    myri10ge_dma_alloc(mgp->dip, bytes,
555210253Sxiuyan.wang@Sun.COM 		    &myri10ge_misc_dma_attr, &myri10ge_dev_access_attr,
555310253Sxiuyan.wang@Sun.COM 		    DDI_DMA_CONSISTENT, DDI_DMA_READ|DDI_DMA_CONSISTENT,
555410253Sxiuyan.wang@Sun.COM 		    &ss->fw_stats_dma, 1, DDI_DMA_DONTWAIT);
555510253Sxiuyan.wang@Sun.COM 		if (ss->fw_stats == NULL)
555610253Sxiuyan.wang@Sun.COM 			goto abort;
555710253Sxiuyan.wang@Sun.COM 		(void) memset(ss->fw_stats, 0, bytes);
555810253Sxiuyan.wang@Sun.COM 
555910253Sxiuyan.wang@Sun.COM 		/* allocate rx done ring */
556010253Sxiuyan.wang@Sun.COM 		bytes = mgp->max_intr_slots *
556110253Sxiuyan.wang@Sun.COM 		    sizeof (*ss->rx_done.entry);
556210253Sxiuyan.wang@Sun.COM 		ss->rx_done.entry = (mcp_slot_t *)(void *)
556310253Sxiuyan.wang@Sun.COM 		    myri10ge_dma_alloc(mgp->dip, bytes,
556410253Sxiuyan.wang@Sun.COM 		    &myri10ge_misc_dma_attr, &myri10ge_dev_access_attr,
556510253Sxiuyan.wang@Sun.COM 		    DDI_DMA_CONSISTENT, DDI_DMA_READ|DDI_DMA_CONSISTENT,
556610253Sxiuyan.wang@Sun.COM 		    &ss->rx_done.dma, 1, DDI_DMA_DONTWAIT);
556710253Sxiuyan.wang@Sun.COM 		if (ss->rx_done.entry == NULL) {
556810253Sxiuyan.wang@Sun.COM 			goto abort;
556910253Sxiuyan.wang@Sun.COM 		}
557010253Sxiuyan.wang@Sun.COM 		(void) memset(ss->rx_done.entry, 0, bytes);
557110253Sxiuyan.wang@Sun.COM 		mutex_init(&ss->rx_lock,   NULL, MUTEX_DEFAULT, mgp->icookie);
557210253Sxiuyan.wang@Sun.COM 		mutex_init(&ss->tx.lock,   NULL, MUTEX_DEFAULT, NULL);
557310253Sxiuyan.wang@Sun.COM 		mutex_init(&ss->tx.handle_lock,   NULL, MUTEX_DEFAULT, NULL);
557410253Sxiuyan.wang@Sun.COM 		mutex_init(&ss->poll_lock,   NULL, MUTEX_DEFAULT, NULL);
557510253Sxiuyan.wang@Sun.COM 		myri10ge_jpool_init(ss);
557610253Sxiuyan.wang@Sun.COM 		(void) myri10ge_slice_stat_init(ss);
557710253Sxiuyan.wang@Sun.COM 		myri10ge_lro_alloc(ss);
557810253Sxiuyan.wang@Sun.COM 	}
557910253Sxiuyan.wang@Sun.COM 
558010253Sxiuyan.wang@Sun.COM 	return (0);
558110253Sxiuyan.wang@Sun.COM 
558210253Sxiuyan.wang@Sun.COM abort:
558310253Sxiuyan.wang@Sun.COM 	myri10ge_free_slices(mgp);
558410253Sxiuyan.wang@Sun.COM 	return (ENOMEM);
558510253Sxiuyan.wang@Sun.COM }
558610253Sxiuyan.wang@Sun.COM 
558710253Sxiuyan.wang@Sun.COM static int
myri10ge_save_msi_state(struct myri10ge_priv * mgp,ddi_acc_handle_t handle)558810253Sxiuyan.wang@Sun.COM myri10ge_save_msi_state(struct myri10ge_priv *mgp,
558910253Sxiuyan.wang@Sun.COM     ddi_acc_handle_t handle)
559010253Sxiuyan.wang@Sun.COM {
559110253Sxiuyan.wang@Sun.COM 	uint8_t ptr;
559210253Sxiuyan.wang@Sun.COM 	int err;
559310253Sxiuyan.wang@Sun.COM 
559410253Sxiuyan.wang@Sun.COM 	err = myri10ge_find_cap(handle, &ptr, PCI_CAP_ID_MSI);
559510253Sxiuyan.wang@Sun.COM 	if (err != 0) {
559610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: could not find MSI cap\n",
559710253Sxiuyan.wang@Sun.COM 		    mgp->name);
559810253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
559910253Sxiuyan.wang@Sun.COM 	}
560010253Sxiuyan.wang@Sun.COM 	mgp->pci_saved_state.msi_ctrl =
560110253Sxiuyan.wang@Sun.COM 	    pci_config_get16(handle, ptr + PCI_MSI_CTRL);
560210253Sxiuyan.wang@Sun.COM 	mgp->pci_saved_state.msi_addr_low =
560310253Sxiuyan.wang@Sun.COM 	    pci_config_get32(handle, ptr + PCI_MSI_ADDR_OFFSET);
560410253Sxiuyan.wang@Sun.COM 	mgp->pci_saved_state.msi_addr_high =
560510253Sxiuyan.wang@Sun.COM 	    pci_config_get32(handle, ptr + PCI_MSI_ADDR_OFFSET + 4);
560610253Sxiuyan.wang@Sun.COM 	mgp->pci_saved_state.msi_data_32 =
560710253Sxiuyan.wang@Sun.COM 	    pci_config_get16(handle, ptr + PCI_MSI_32BIT_DATA);
560810253Sxiuyan.wang@Sun.COM 	mgp->pci_saved_state.msi_data_64 =
560910253Sxiuyan.wang@Sun.COM 	    pci_config_get16(handle, ptr + PCI_MSI_64BIT_DATA);
561010253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
561110253Sxiuyan.wang@Sun.COM }
561210253Sxiuyan.wang@Sun.COM 
561310253Sxiuyan.wang@Sun.COM static int
myri10ge_restore_msi_state(struct myri10ge_priv * mgp,ddi_acc_handle_t handle)561410253Sxiuyan.wang@Sun.COM myri10ge_restore_msi_state(struct myri10ge_priv *mgp,
561510253Sxiuyan.wang@Sun.COM     ddi_acc_handle_t handle)
561610253Sxiuyan.wang@Sun.COM {
561710253Sxiuyan.wang@Sun.COM 	uint8_t ptr;
561810253Sxiuyan.wang@Sun.COM 	int err;
561910253Sxiuyan.wang@Sun.COM 
562010253Sxiuyan.wang@Sun.COM 	err = myri10ge_find_cap(handle, &ptr, PCI_CAP_ID_MSI);
562110253Sxiuyan.wang@Sun.COM 	if (err != 0) {
562210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: could not find MSI cap\n",
562310253Sxiuyan.wang@Sun.COM 		    mgp->name);
562410253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
562510253Sxiuyan.wang@Sun.COM 	}
562610253Sxiuyan.wang@Sun.COM 
562710253Sxiuyan.wang@Sun.COM 	pci_config_put16(handle, ptr + PCI_MSI_CTRL,
562810253Sxiuyan.wang@Sun.COM 	    mgp->pci_saved_state.msi_ctrl);
562910253Sxiuyan.wang@Sun.COM 	pci_config_put32(handle, ptr + PCI_MSI_ADDR_OFFSET,
563010253Sxiuyan.wang@Sun.COM 	    mgp->pci_saved_state.msi_addr_low);
563110253Sxiuyan.wang@Sun.COM 	pci_config_put32(handle, ptr + PCI_MSI_ADDR_OFFSET + 4,
563210253Sxiuyan.wang@Sun.COM 	    mgp->pci_saved_state.msi_addr_high);
563310253Sxiuyan.wang@Sun.COM 	pci_config_put16(handle, ptr + PCI_MSI_32BIT_DATA,
563410253Sxiuyan.wang@Sun.COM 	    mgp->pci_saved_state.msi_data_32);
563510253Sxiuyan.wang@Sun.COM 	pci_config_put16(handle, ptr + PCI_MSI_64BIT_DATA,
563610253Sxiuyan.wang@Sun.COM 	    mgp->pci_saved_state.msi_data_64);
563710253Sxiuyan.wang@Sun.COM 
563810253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
563910253Sxiuyan.wang@Sun.COM }
564010253Sxiuyan.wang@Sun.COM 
564110253Sxiuyan.wang@Sun.COM static int
myri10ge_save_pci_state(struct myri10ge_priv * mgp)564210253Sxiuyan.wang@Sun.COM myri10ge_save_pci_state(struct myri10ge_priv *mgp)
564310253Sxiuyan.wang@Sun.COM {
564410253Sxiuyan.wang@Sun.COM 	ddi_acc_handle_t handle = mgp->cfg_hdl;
564510253Sxiuyan.wang@Sun.COM 	int i;
564610253Sxiuyan.wang@Sun.COM 	int err = DDI_SUCCESS;
564710253Sxiuyan.wang@Sun.COM 
564810253Sxiuyan.wang@Sun.COM 
564910253Sxiuyan.wang@Sun.COM 	/* Save the non-extended PCI config space 32-bits at a time */
565010253Sxiuyan.wang@Sun.COM 	for (i = 0; i < 16; i++)
565110253Sxiuyan.wang@Sun.COM 		mgp->pci_saved_state.base[i] =
565210253Sxiuyan.wang@Sun.COM 		    pci_config_get32(handle, i*4);
565310253Sxiuyan.wang@Sun.COM 
565410253Sxiuyan.wang@Sun.COM 	/* now save MSI interrupt state *, if needed */
565510253Sxiuyan.wang@Sun.COM 	if (mgp->ddi_intr_type == DDI_INTR_TYPE_MSI)
565610253Sxiuyan.wang@Sun.COM 		err = myri10ge_save_msi_state(mgp, handle);
565710253Sxiuyan.wang@Sun.COM 
565810253Sxiuyan.wang@Sun.COM 	return (err);
565910253Sxiuyan.wang@Sun.COM }
566010253Sxiuyan.wang@Sun.COM 
566110253Sxiuyan.wang@Sun.COM static int
myri10ge_restore_pci_state(struct myri10ge_priv * mgp)566210253Sxiuyan.wang@Sun.COM myri10ge_restore_pci_state(struct myri10ge_priv *mgp)
566310253Sxiuyan.wang@Sun.COM {
566410253Sxiuyan.wang@Sun.COM 	ddi_acc_handle_t handle = mgp->cfg_hdl;
566510253Sxiuyan.wang@Sun.COM 	int i;
566610253Sxiuyan.wang@Sun.COM 	int err = DDI_SUCCESS;
566710253Sxiuyan.wang@Sun.COM 
566810253Sxiuyan.wang@Sun.COM 
566910253Sxiuyan.wang@Sun.COM 	/* Restore the non-extended PCI config space 32-bits at a time */
567010253Sxiuyan.wang@Sun.COM 	for (i = 15; i >= 0; i--)
567110253Sxiuyan.wang@Sun.COM 		pci_config_put32(handle, i*4, mgp->pci_saved_state.base[i]);
567210253Sxiuyan.wang@Sun.COM 
567310253Sxiuyan.wang@Sun.COM 	/* now restore MSI interrupt state *, if needed */
567410253Sxiuyan.wang@Sun.COM 	if (mgp->ddi_intr_type == DDI_INTR_TYPE_MSI)
567510253Sxiuyan.wang@Sun.COM 		err = myri10ge_restore_msi_state(mgp, handle);
567610253Sxiuyan.wang@Sun.COM 
567710253Sxiuyan.wang@Sun.COM 	if (mgp->max_read_request_4k)
567810253Sxiuyan.wang@Sun.COM 		(void) myri10ge_set_max_readreq(handle);
567910253Sxiuyan.wang@Sun.COM 	return (err);
568010253Sxiuyan.wang@Sun.COM }
568110253Sxiuyan.wang@Sun.COM 
568210253Sxiuyan.wang@Sun.COM 
568310253Sxiuyan.wang@Sun.COM static int
myri10ge_suspend(dev_info_t * dip)568410253Sxiuyan.wang@Sun.COM myri10ge_suspend(dev_info_t *dip)
568510253Sxiuyan.wang@Sun.COM {
568610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ddi_get_driver_private(dip);
568710253Sxiuyan.wang@Sun.COM 	int status;
568810253Sxiuyan.wang@Sun.COM 
568910253Sxiuyan.wang@Sun.COM 	if (mgp == NULL) {
569010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "null dip in myri10ge_suspend\n");
569110253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
569210253Sxiuyan.wang@Sun.COM 	}
569310253Sxiuyan.wang@Sun.COM 	if (mgp->dip != dip) {
569410253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "bad dip in myri10ge_suspend\n");
569510253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
569610253Sxiuyan.wang@Sun.COM 	}
569710253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
569810253Sxiuyan.wang@Sun.COM 	if (mgp->running == MYRI10GE_ETH_RUNNING) {
569910253Sxiuyan.wang@Sun.COM 		mgp->running = MYRI10GE_ETH_STOPPING;
570010253Sxiuyan.wang@Sun.COM 		mutex_exit(&mgp->intrlock);
570110253Sxiuyan.wang@Sun.COM 		(void) untimeout(mgp->timer_id);
570210253Sxiuyan.wang@Sun.COM 		mutex_enter(&mgp->intrlock);
570310253Sxiuyan.wang@Sun.COM 		myri10ge_stop_locked(mgp);
570410253Sxiuyan.wang@Sun.COM 		mgp->running = MYRI10GE_ETH_SUSPENDED_RUNNING;
570510253Sxiuyan.wang@Sun.COM 	}
570610253Sxiuyan.wang@Sun.COM 	status = myri10ge_save_pci_state(mgp);
570710253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
570810253Sxiuyan.wang@Sun.COM 	return (status);
570910253Sxiuyan.wang@Sun.COM }
571010253Sxiuyan.wang@Sun.COM 
571110253Sxiuyan.wang@Sun.COM static int
myri10ge_resume(dev_info_t * dip)571210253Sxiuyan.wang@Sun.COM myri10ge_resume(dev_info_t *dip)
571310253Sxiuyan.wang@Sun.COM {
571410253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp = ddi_get_driver_private(dip);
571510253Sxiuyan.wang@Sun.COM 	int status = DDI_SUCCESS;
571610253Sxiuyan.wang@Sun.COM 
571710253Sxiuyan.wang@Sun.COM 	if (mgp == NULL) {
571810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "null dip in myri10ge_resume\n");
571910253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
572010253Sxiuyan.wang@Sun.COM 	}
572110253Sxiuyan.wang@Sun.COM 	if (mgp->dip != dip) {
572210253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "bad dip in myri10ge_resume\n");
572310253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
572410253Sxiuyan.wang@Sun.COM 	}
572510253Sxiuyan.wang@Sun.COM 
572610253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
572710253Sxiuyan.wang@Sun.COM 	status = myri10ge_restore_pci_state(mgp);
572810253Sxiuyan.wang@Sun.COM 	if (status == DDI_SUCCESS &&
572910253Sxiuyan.wang@Sun.COM 	    mgp->running == MYRI10GE_ETH_SUSPENDED_RUNNING) {
573010253Sxiuyan.wang@Sun.COM 		status = myri10ge_start_locked(mgp);
573110253Sxiuyan.wang@Sun.COM 	}
573210253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
573310253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS)
573410253Sxiuyan.wang@Sun.COM 		return (status);
573510253Sxiuyan.wang@Sun.COM 
573610253Sxiuyan.wang@Sun.COM 	/* start the watchdog timer */
573710253Sxiuyan.wang@Sun.COM 	mgp->timer_id = timeout(myri10ge_watchdog, mgp,
573810253Sxiuyan.wang@Sun.COM 	    mgp->timer_ticks);
573910253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
574010253Sxiuyan.wang@Sun.COM }
574110253Sxiuyan.wang@Sun.COM 
574210253Sxiuyan.wang@Sun.COM static int
myri10ge_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)574310253Sxiuyan.wang@Sun.COM myri10ge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
574410253Sxiuyan.wang@Sun.COM {
574510253Sxiuyan.wang@Sun.COM 
574610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
574710253Sxiuyan.wang@Sun.COM 	mac_register_t *macp, *omacp;
574810253Sxiuyan.wang@Sun.COM 	ddi_acc_handle_t handle;
574910253Sxiuyan.wang@Sun.COM 	uint32_t csr, hdr_offset;
575010253Sxiuyan.wang@Sun.COM 	int status, span, link_width, max_read_request_4k;
575110253Sxiuyan.wang@Sun.COM 	unsigned long bus_number, dev_number, func_number;
575210253Sxiuyan.wang@Sun.COM 	size_t bytes;
575310253Sxiuyan.wang@Sun.COM 	offset_t ss_offset;
575410253Sxiuyan.wang@Sun.COM 	uint8_t vso;
575510253Sxiuyan.wang@Sun.COM 
575610253Sxiuyan.wang@Sun.COM 	if (cmd == DDI_RESUME) {
575710253Sxiuyan.wang@Sun.COM 		return (myri10ge_resume(dip));
575810253Sxiuyan.wang@Sun.COM 	}
575910253Sxiuyan.wang@Sun.COM 
576010253Sxiuyan.wang@Sun.COM 	if (cmd != DDI_ATTACH)
576110253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
576210253Sxiuyan.wang@Sun.COM 	if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
576310253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
576410253Sxiuyan.wang@Sun.COM 
576510253Sxiuyan.wang@Sun.COM 	/* enable busmater and io space access */
576610253Sxiuyan.wang@Sun.COM 	csr = pci_config_get32(handle, PCI_CONF_COMM);
576710253Sxiuyan.wang@Sun.COM 	pci_config_put32(handle, PCI_CONF_COMM,
576810253Sxiuyan.wang@Sun.COM 	    (csr |PCI_COMM_ME|PCI_COMM_MAE));
576910253Sxiuyan.wang@Sun.COM 	status = myri10ge_read_pcie_link_width(handle, &link_width);
577010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
577110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "could not read link width!\n");
577210253Sxiuyan.wang@Sun.COM 		link_width = 0;
577310253Sxiuyan.wang@Sun.COM 	}
577410253Sxiuyan.wang@Sun.COM 	max_read_request_4k = !myri10ge_set_max_readreq(handle);
577510253Sxiuyan.wang@Sun.COM 	status = myri10ge_find_cap(handle, &vso, PCI_CAP_ID_VS);
577610253Sxiuyan.wang@Sun.COM 	if (status != 0)
577710253Sxiuyan.wang@Sun.COM 		goto abort_with_cfg_hdl;
577810253Sxiuyan.wang@Sun.COM 	if ((omacp = mac_alloc(MAC_VERSION)) == NULL)
577910253Sxiuyan.wang@Sun.COM 		goto abort_with_cfg_hdl;
578010253Sxiuyan.wang@Sun.COM 	/*
578110253Sxiuyan.wang@Sun.COM 	 * XXXX Hack: mac_register_t grows in newer kernels.  To be
578210253Sxiuyan.wang@Sun.COM 	 * able to write newer fields, such as m_margin, without
578310253Sxiuyan.wang@Sun.COM 	 * writing outside allocated memory, we allocate our own macp
578410253Sxiuyan.wang@Sun.COM 	 * and pass that to mac_register()
578510253Sxiuyan.wang@Sun.COM 	 */
578610253Sxiuyan.wang@Sun.COM 	macp = kmem_zalloc(sizeof (*macp) * 8, KM_SLEEP);
578710253Sxiuyan.wang@Sun.COM 	macp->m_version = omacp->m_version;
578810253Sxiuyan.wang@Sun.COM 
578910253Sxiuyan.wang@Sun.COM 	if ((mgp = (struct myri10ge_priv *)
579010253Sxiuyan.wang@Sun.COM 	    kmem_zalloc(sizeof (*mgp), KM_SLEEP)) == NULL) {
579110253Sxiuyan.wang@Sun.COM 		goto abort_with_macinfo;
579210253Sxiuyan.wang@Sun.COM 	}
579310253Sxiuyan.wang@Sun.COM 	ddi_set_driver_private(dip, mgp);
579410253Sxiuyan.wang@Sun.COM 
579510253Sxiuyan.wang@Sun.COM 	/* setup device name for log messages */
579610253Sxiuyan.wang@Sun.COM 	(void) sprintf(mgp->name, "myri10ge%d", ddi_get_instance(dip));
579710253Sxiuyan.wang@Sun.COM 
579810253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
579910253Sxiuyan.wang@Sun.COM 	myri10ge_get_props(dip);
580010253Sxiuyan.wang@Sun.COM 	mgp->intr_coal_delay = myri10ge_intr_coal_delay;
580110253Sxiuyan.wang@Sun.COM 	mgp->pause = myri10ge_flow_control;
580210253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
580310253Sxiuyan.wang@Sun.COM 
580410253Sxiuyan.wang@Sun.COM 	mgp->max_read_request_4k = max_read_request_4k;
580510253Sxiuyan.wang@Sun.COM 	mgp->pcie_link_width = link_width;
580610253Sxiuyan.wang@Sun.COM 	mgp->running = MYRI10GE_ETH_STOPPED;
580710253Sxiuyan.wang@Sun.COM 	mgp->vso = vso;
580810253Sxiuyan.wang@Sun.COM 	mgp->dip = dip;
580910253Sxiuyan.wang@Sun.COM 	mgp->cfg_hdl = handle;
581010253Sxiuyan.wang@Sun.COM 
581110253Sxiuyan.wang@Sun.COM 	mgp->timer_ticks = 5 * drv_usectohz(1000000); /* 5 seconds */
581210253Sxiuyan.wang@Sun.COM 	myri10ge_test_physical(dip);
581310253Sxiuyan.wang@Sun.COM 
581410253Sxiuyan.wang@Sun.COM 	/* allocate command page */
581510253Sxiuyan.wang@Sun.COM 	bytes = sizeof (*mgp->cmd);
581610253Sxiuyan.wang@Sun.COM 	mgp->cmd = (mcp_cmd_response_t *)
581710253Sxiuyan.wang@Sun.COM 	    (void *)myri10ge_dma_alloc(dip, bytes,
581810253Sxiuyan.wang@Sun.COM 	    &myri10ge_misc_dma_attr, &myri10ge_dev_access_attr,
581910253Sxiuyan.wang@Sun.COM 	    DDI_DMA_CONSISTENT,	DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
582010253Sxiuyan.wang@Sun.COM 	    &mgp->cmd_dma, 1, DDI_DMA_DONTWAIT);
582110253Sxiuyan.wang@Sun.COM 	if (mgp->cmd == NULL)
582210253Sxiuyan.wang@Sun.COM 		goto abort_with_mgp;
582310253Sxiuyan.wang@Sun.COM 
582410253Sxiuyan.wang@Sun.COM 	(void) myri10ge_reg_set(dip, &mgp->reg_set, &span, &bus_number,
582510253Sxiuyan.wang@Sun.COM 	    &dev_number, &func_number);
582610253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
582710253Sxiuyan.wang@Sun.COM 		printf("%s at %ld:%ld:%ld attaching\n", mgp->name,
582810253Sxiuyan.wang@Sun.COM 		    bus_number, dev_number, func_number);
582910253Sxiuyan.wang@Sun.COM 	status = ddi_regs_map_setup(dip, mgp->reg_set, (caddr_t *)&mgp->sram,
583010253Sxiuyan.wang@Sun.COM 	    (offset_t)0, (offset_t)span,  &myri10ge_dev_access_attr,
583110253Sxiuyan.wang@Sun.COM 	    &mgp->io_handle);
583210253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS) {
583310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: couldn't map memory space", mgp->name);
583410253Sxiuyan.wang@Sun.COM 		printf("%s: reg_set = %d, span = %d, status = %d",
583510253Sxiuyan.wang@Sun.COM 		    mgp->name, mgp->reg_set, span, status);
583610253Sxiuyan.wang@Sun.COM 		goto abort_with_mgp;
583710253Sxiuyan.wang@Sun.COM 	}
583810253Sxiuyan.wang@Sun.COM 
583910253Sxiuyan.wang@Sun.COM 	hdr_offset = *(uint32_t *)(void*)(mgp->sram +  MCP_HEADER_PTR_OFFSET);
584010253Sxiuyan.wang@Sun.COM 	hdr_offset = ntohl(hdr_offset) & 0xffffc;
584110253Sxiuyan.wang@Sun.COM 	ss_offset = hdr_offset +
584210253Sxiuyan.wang@Sun.COM 	    offsetof(struct mcp_gen_header, string_specs);
584310253Sxiuyan.wang@Sun.COM 	mgp->sram_size = ntohl(*(uint32_t *)(void*)(mgp->sram + ss_offset));
584410253Sxiuyan.wang@Sun.COM 	myri10ge_pio_copy32(mgp->eeprom_strings,
584510253Sxiuyan.wang@Sun.COM 	    (uint32_t *)(void*)((char *)mgp->sram + mgp->sram_size),
584610253Sxiuyan.wang@Sun.COM 	    MYRI10GE_EEPROM_STRINGS_SIZE);
584710253Sxiuyan.wang@Sun.COM 	(void) memset(mgp->eeprom_strings +
584810253Sxiuyan.wang@Sun.COM 	    MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2);
584910253Sxiuyan.wang@Sun.COM 
585010253Sxiuyan.wang@Sun.COM 	status = myri10ge_read_mac_addr(mgp);
585110253Sxiuyan.wang@Sun.COM 	if (status) {
585210253Sxiuyan.wang@Sun.COM 		goto abort_with_mapped;
585310253Sxiuyan.wang@Sun.COM 	}
585410253Sxiuyan.wang@Sun.COM 
585510253Sxiuyan.wang@Sun.COM 	status = myri10ge_select_firmware(mgp);
585610253Sxiuyan.wang@Sun.COM 	if (status != 0) {
585710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed to load firmware\n", mgp->name);
585810253Sxiuyan.wang@Sun.COM 		goto abort_with_mapped;
585910253Sxiuyan.wang@Sun.COM 	}
586010253Sxiuyan.wang@Sun.COM 
586110253Sxiuyan.wang@Sun.COM 	status = myri10ge_probe_slices(mgp);
586210253Sxiuyan.wang@Sun.COM 	if (status != 0) {
586310253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed to probe slices\n", mgp->name);
586410253Sxiuyan.wang@Sun.COM 		goto abort_with_dummy_rdma;
586510253Sxiuyan.wang@Sun.COM 	}
586610253Sxiuyan.wang@Sun.COM 
586710253Sxiuyan.wang@Sun.COM 	status = myri10ge_alloc_slices(mgp);
586810253Sxiuyan.wang@Sun.COM 	if (status != 0) {
586910253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: failed to alloc slices\n", mgp->name);
587010253Sxiuyan.wang@Sun.COM 		goto abort_with_dummy_rdma;
587110253Sxiuyan.wang@Sun.COM 	}
587210253Sxiuyan.wang@Sun.COM 
587310253Sxiuyan.wang@Sun.COM 	/* add the interrupt handler */
587410253Sxiuyan.wang@Sun.COM 	status = myri10ge_add_intrs(mgp, 1);
587510253Sxiuyan.wang@Sun.COM 	if (status != 0) {
587610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Failed to add interrupt\n",
587710253Sxiuyan.wang@Sun.COM 		    mgp->name);
587810253Sxiuyan.wang@Sun.COM 		goto abort_with_slices;
587910253Sxiuyan.wang@Sun.COM 	}
588010253Sxiuyan.wang@Sun.COM 
588110253Sxiuyan.wang@Sun.COM 	/* now that we have an iblock_cookie, init the mutexes */
588210253Sxiuyan.wang@Sun.COM 	mutex_init(&mgp->cmd_lock, NULL, MUTEX_DRIVER, mgp->icookie);
588310253Sxiuyan.wang@Sun.COM 	mutex_init(&mgp->intrlock, NULL, MUTEX_DRIVER, mgp->icookie);
588410253Sxiuyan.wang@Sun.COM 
588510253Sxiuyan.wang@Sun.COM 
588610253Sxiuyan.wang@Sun.COM 	status = myri10ge_nic_stat_init(mgp);
588710253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS)
588810253Sxiuyan.wang@Sun.COM 		goto abort_with_interrupts;
588910253Sxiuyan.wang@Sun.COM 	status = myri10ge_info_init(mgp);
589010253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS)
589110253Sxiuyan.wang@Sun.COM 		goto abort_with_stats;
589210253Sxiuyan.wang@Sun.COM 
589310253Sxiuyan.wang@Sun.COM 	/*
589410253Sxiuyan.wang@Sun.COM 	 *	Initialize  GLD state
589510253Sxiuyan.wang@Sun.COM 	 */
589610253Sxiuyan.wang@Sun.COM 
589710253Sxiuyan.wang@Sun.COM 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
589810253Sxiuyan.wang@Sun.COM 	macp->m_driver = mgp;
589910253Sxiuyan.wang@Sun.COM 	macp->m_dip = dip;
590010253Sxiuyan.wang@Sun.COM 	macp->m_src_addr = mgp->mac_addr;
590110253Sxiuyan.wang@Sun.COM 	macp->m_callbacks = &myri10ge_m_callbacks;
590210253Sxiuyan.wang@Sun.COM 	macp->m_min_sdu = 0;
590310253Sxiuyan.wang@Sun.COM 	macp->m_max_sdu = myri10ge_mtu -
590410253Sxiuyan.wang@Sun.COM 	    (sizeof (struct ether_header) + MXGEFW_PAD + VLAN_TAGSZ);
590510253Sxiuyan.wang@Sun.COM #ifdef SOLARIS_S11
590610253Sxiuyan.wang@Sun.COM 	macp->m_margin = VLAN_TAGSZ;
590710253Sxiuyan.wang@Sun.COM #endif
590810253Sxiuyan.wang@Sun.COM 	macp->m_v12n = MAC_VIRT_LEVEL1;
590910253Sxiuyan.wang@Sun.COM 	status = mac_register(macp, &mgp->mh);
591010253Sxiuyan.wang@Sun.COM 	if (status != 0) {
591110253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: mac_register failed with %d\n",
591210253Sxiuyan.wang@Sun.COM 		    mgp->name, status);
591310253Sxiuyan.wang@Sun.COM 		goto abort_with_info;
591410253Sxiuyan.wang@Sun.COM 	}
591510253Sxiuyan.wang@Sun.COM 	myri10ge_ndd_init(mgp);
591610253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
591710253Sxiuyan.wang@Sun.COM 		printf("%s: %s, tx bndry %d, fw %s\n", mgp->name,
591810253Sxiuyan.wang@Sun.COM 		    mgp->intr_type, mgp->tx_boundary, mgp->fw_name);
591910253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
592010253Sxiuyan.wang@Sun.COM 	mgp->next = mgp_list;
592110253Sxiuyan.wang@Sun.COM 	mgp_list = mgp;
592210253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
592310253Sxiuyan.wang@Sun.COM 	kmem_free(macp, sizeof (*macp) * 8);
592410253Sxiuyan.wang@Sun.COM 	mac_free(omacp);
592510253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
592610253Sxiuyan.wang@Sun.COM 
592710253Sxiuyan.wang@Sun.COM abort_with_info:
592810253Sxiuyan.wang@Sun.COM 	myri10ge_info_destroy(mgp);
592910253Sxiuyan.wang@Sun.COM 
593010253Sxiuyan.wang@Sun.COM abort_with_stats:
593110253Sxiuyan.wang@Sun.COM 	myri10ge_nic_stat_destroy(mgp);
593210253Sxiuyan.wang@Sun.COM 
593310253Sxiuyan.wang@Sun.COM abort_with_interrupts:
593410253Sxiuyan.wang@Sun.COM 	mutex_destroy(&mgp->cmd_lock);
593510253Sxiuyan.wang@Sun.COM 	mutex_destroy(&mgp->intrlock);
593610253Sxiuyan.wang@Sun.COM 	myri10ge_rem_intrs(mgp, 1);
593710253Sxiuyan.wang@Sun.COM 
593810253Sxiuyan.wang@Sun.COM abort_with_slices:
593910253Sxiuyan.wang@Sun.COM 	myri10ge_free_slices(mgp);
594010253Sxiuyan.wang@Sun.COM 
594110253Sxiuyan.wang@Sun.COM abort_with_dummy_rdma:
594210253Sxiuyan.wang@Sun.COM 	myri10ge_dummy_rdma(mgp, 0);
594310253Sxiuyan.wang@Sun.COM 
594410253Sxiuyan.wang@Sun.COM abort_with_mapped:
594510253Sxiuyan.wang@Sun.COM 	ddi_regs_map_free(&mgp->io_handle);
594610253Sxiuyan.wang@Sun.COM 
594710253Sxiuyan.wang@Sun.COM 	myri10ge_dma_free(&mgp->cmd_dma);
594810253Sxiuyan.wang@Sun.COM 
594910253Sxiuyan.wang@Sun.COM abort_with_mgp:
595010253Sxiuyan.wang@Sun.COM 	kmem_free(mgp, sizeof (*mgp));
595110253Sxiuyan.wang@Sun.COM 
595210253Sxiuyan.wang@Sun.COM abort_with_macinfo:
595310253Sxiuyan.wang@Sun.COM 	kmem_free(macp, sizeof (*macp) * 8);
595410253Sxiuyan.wang@Sun.COM 	mac_free(omacp);
595510253Sxiuyan.wang@Sun.COM 
595610253Sxiuyan.wang@Sun.COM abort_with_cfg_hdl:
595710253Sxiuyan.wang@Sun.COM 	pci_config_teardown(&handle);
595810253Sxiuyan.wang@Sun.COM 	return (DDI_FAILURE);
595910253Sxiuyan.wang@Sun.COM 
596010253Sxiuyan.wang@Sun.COM }
596110253Sxiuyan.wang@Sun.COM 
596210253Sxiuyan.wang@Sun.COM 
596310253Sxiuyan.wang@Sun.COM static int
myri10ge_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)596410253Sxiuyan.wang@Sun.COM myri10ge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
596510253Sxiuyan.wang@Sun.COM {
596610253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv	*mgp, *tmp;
596710253Sxiuyan.wang@Sun.COM 	int 			status, i, jbufs_alloced;
596810253Sxiuyan.wang@Sun.COM 
596910253Sxiuyan.wang@Sun.COM 	if (cmd == DDI_SUSPEND) {
597010253Sxiuyan.wang@Sun.COM 		status = myri10ge_suspend(dip);
597110253Sxiuyan.wang@Sun.COM 		return (status);
597210253Sxiuyan.wang@Sun.COM 	}
597310253Sxiuyan.wang@Sun.COM 
597410253Sxiuyan.wang@Sun.COM 	if (cmd != DDI_DETACH) {
597510253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
597610253Sxiuyan.wang@Sun.COM 	}
597710253Sxiuyan.wang@Sun.COM 	/* Get the driver private (gld_mac_info_t) structure */
597810253Sxiuyan.wang@Sun.COM 	mgp = ddi_get_driver_private(dip);
597910253Sxiuyan.wang@Sun.COM 
598010253Sxiuyan.wang@Sun.COM 	mutex_enter(&mgp->intrlock);
598110253Sxiuyan.wang@Sun.COM 	jbufs_alloced = 0;
598210253Sxiuyan.wang@Sun.COM 	for (i = 0; i < mgp->num_slices; i++) {
598310253Sxiuyan.wang@Sun.COM 		myri10ge_remove_jbufs(&mgp->ss[i]);
598410253Sxiuyan.wang@Sun.COM 		jbufs_alloced += mgp->ss[i].jpool.num_alloc;
598510253Sxiuyan.wang@Sun.COM 	}
598610253Sxiuyan.wang@Sun.COM 	mutex_exit(&mgp->intrlock);
598710253Sxiuyan.wang@Sun.COM 	if (jbufs_alloced != 0) {
598810253Sxiuyan.wang@Sun.COM 		cmn_err(CE_NOTE, "%s: %d loaned rx buffers remain\n",
598910253Sxiuyan.wang@Sun.COM 		    mgp->name, jbufs_alloced);
599010253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
599110253Sxiuyan.wang@Sun.COM 	}
599210253Sxiuyan.wang@Sun.COM 
599310253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
599410253Sxiuyan.wang@Sun.COM 	if (mgp->refcnt != 0) {
599510253Sxiuyan.wang@Sun.COM 		mutex_exit(&myri10ge_param_lock);
599610253Sxiuyan.wang@Sun.COM 		cmn_err(CE_NOTE, "%s: %d external refs remain\n",
599710253Sxiuyan.wang@Sun.COM 		    mgp->name, mgp->refcnt);
599810253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
599910253Sxiuyan.wang@Sun.COM 	}
600010253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
600110253Sxiuyan.wang@Sun.COM 
600210253Sxiuyan.wang@Sun.COM 	status = mac_unregister(mgp->mh);
600310253Sxiuyan.wang@Sun.COM 	if (status != DDI_SUCCESS)
600410253Sxiuyan.wang@Sun.COM 		return (status);
600510253Sxiuyan.wang@Sun.COM 
600610253Sxiuyan.wang@Sun.COM 	myri10ge_ndd_fini(mgp);
600710253Sxiuyan.wang@Sun.COM 	myri10ge_dummy_rdma(mgp, 0);
600810253Sxiuyan.wang@Sun.COM 	myri10ge_nic_stat_destroy(mgp);
600910253Sxiuyan.wang@Sun.COM 	myri10ge_info_destroy(mgp);
601010253Sxiuyan.wang@Sun.COM 
601110253Sxiuyan.wang@Sun.COM 	mutex_destroy(&mgp->cmd_lock);
601210253Sxiuyan.wang@Sun.COM 	mutex_destroy(&mgp->intrlock);
601310253Sxiuyan.wang@Sun.COM 
601410253Sxiuyan.wang@Sun.COM 	myri10ge_rem_intrs(mgp, 1);
601510253Sxiuyan.wang@Sun.COM 
601610253Sxiuyan.wang@Sun.COM 	myri10ge_free_slices(mgp);
601710253Sxiuyan.wang@Sun.COM 	ddi_regs_map_free(&mgp->io_handle);
601810253Sxiuyan.wang@Sun.COM 	myri10ge_dma_free(&mgp->cmd_dma);
601910253Sxiuyan.wang@Sun.COM 	pci_config_teardown(&mgp->cfg_hdl);
602010253Sxiuyan.wang@Sun.COM 
602110253Sxiuyan.wang@Sun.COM 	mutex_enter(&myri10ge_param_lock);
602210253Sxiuyan.wang@Sun.COM 	if (mgp_list == mgp) {
602310253Sxiuyan.wang@Sun.COM 		mgp_list = mgp->next;
602410253Sxiuyan.wang@Sun.COM 	} else {
602510253Sxiuyan.wang@Sun.COM 		tmp = mgp_list;
602610253Sxiuyan.wang@Sun.COM 		while (tmp->next != mgp && tmp->next != NULL)
602710253Sxiuyan.wang@Sun.COM 			tmp = tmp->next;
602810253Sxiuyan.wang@Sun.COM 		if (tmp->next != NULL)
602910253Sxiuyan.wang@Sun.COM 			tmp->next = tmp->next->next;
603010253Sxiuyan.wang@Sun.COM 	}
603110253Sxiuyan.wang@Sun.COM 	kmem_free(mgp, sizeof (*mgp));
603210253Sxiuyan.wang@Sun.COM 	mutex_exit(&myri10ge_param_lock);
603310253Sxiuyan.wang@Sun.COM 	return (DDI_SUCCESS);
603410253Sxiuyan.wang@Sun.COM }
603510253Sxiuyan.wang@Sun.COM 
603610253Sxiuyan.wang@Sun.COM /*
603710253Sxiuyan.wang@Sun.COM  * Helper for quiesce entry point: Interrupt threads are not being
603810253Sxiuyan.wang@Sun.COM  * scheduled, so we must poll for the confirmation DMA to arrive in
603910253Sxiuyan.wang@Sun.COM  * the firmware stats block for slice 0.  We're essentially running
604010253Sxiuyan.wang@Sun.COM  * the guts of the interrupt handler, and just cherry picking the
604110253Sxiuyan.wang@Sun.COM  * confirmation that the NIC is queuesced (stats->link_down)
604210253Sxiuyan.wang@Sun.COM  */
604310253Sxiuyan.wang@Sun.COM 
604410253Sxiuyan.wang@Sun.COM static int
myri10ge_poll_down(struct myri10ge_priv * mgp)604510253Sxiuyan.wang@Sun.COM myri10ge_poll_down(struct myri10ge_priv *mgp)
604610253Sxiuyan.wang@Sun.COM {
604710253Sxiuyan.wang@Sun.COM 	struct myri10ge_slice_state *ss = mgp->ss;
604810253Sxiuyan.wang@Sun.COM 	mcp_irq_data_t *stats = ss->fw_stats;
604910253Sxiuyan.wang@Sun.COM 	int valid;
605010253Sxiuyan.wang@Sun.COM 	int found_down = 0;
605110253Sxiuyan.wang@Sun.COM 
605210253Sxiuyan.wang@Sun.COM 
605310253Sxiuyan.wang@Sun.COM 	/* check for a pending IRQ */
605410253Sxiuyan.wang@Sun.COM 
605510253Sxiuyan.wang@Sun.COM 	if (! *((volatile uint8_t *)& stats->valid))
605610253Sxiuyan.wang@Sun.COM 		return (0);
605710253Sxiuyan.wang@Sun.COM 	valid = stats->valid;
605810253Sxiuyan.wang@Sun.COM 
605910253Sxiuyan.wang@Sun.COM 	/*
606010253Sxiuyan.wang@Sun.COM 	 * Make sure to tell the NIC to lower a legacy IRQ, else
606110253Sxiuyan.wang@Sun.COM 	 * it may have corrupt state after restarting
606210253Sxiuyan.wang@Sun.COM 	 */
606310253Sxiuyan.wang@Sun.COM 
606410253Sxiuyan.wang@Sun.COM 	if (mgp->ddi_intr_type == DDI_INTR_TYPE_FIXED) {
606510253Sxiuyan.wang@Sun.COM 		/* lower legacy IRQ  */
606610253Sxiuyan.wang@Sun.COM 		*mgp->irq_deassert = 0;
606710253Sxiuyan.wang@Sun.COM 		mb();
606810253Sxiuyan.wang@Sun.COM 		/* wait for irq conf DMA */
606910253Sxiuyan.wang@Sun.COM 		while (*((volatile uint8_t *)& stats->valid))
607010253Sxiuyan.wang@Sun.COM 			;
607110253Sxiuyan.wang@Sun.COM 	}
607210253Sxiuyan.wang@Sun.COM 	if (stats->stats_updated && stats->link_down)
607310253Sxiuyan.wang@Sun.COM 		found_down = 1;
607410253Sxiuyan.wang@Sun.COM 
607510253Sxiuyan.wang@Sun.COM 	if (valid & 0x1)
607610253Sxiuyan.wang@Sun.COM 		*ss->irq_claim = BE_32(3);
607710253Sxiuyan.wang@Sun.COM 	*(ss->irq_claim + 1) = BE_32(3);
607810253Sxiuyan.wang@Sun.COM 
607910253Sxiuyan.wang@Sun.COM 	return (found_down);
608010253Sxiuyan.wang@Sun.COM }
608110253Sxiuyan.wang@Sun.COM 
608210253Sxiuyan.wang@Sun.COM static int
myri10ge_quiesce(dev_info_t * dip)608310253Sxiuyan.wang@Sun.COM myri10ge_quiesce(dev_info_t *dip)
608410253Sxiuyan.wang@Sun.COM {
608510253Sxiuyan.wang@Sun.COM 	struct myri10ge_priv *mgp;
608610253Sxiuyan.wang@Sun.COM 	myri10ge_cmd_t cmd;
608710253Sxiuyan.wang@Sun.COM 	int status, down, i;
608810253Sxiuyan.wang@Sun.COM 
608910253Sxiuyan.wang@Sun.COM 	mgp = ddi_get_driver_private(dip);
609010253Sxiuyan.wang@Sun.COM 	if (mgp == NULL)
609110253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
609210253Sxiuyan.wang@Sun.COM 
609310253Sxiuyan.wang@Sun.COM 	/* if devices was unplumbed, it is guaranteed to be quiescent */
609410253Sxiuyan.wang@Sun.COM 	if (mgp->running == MYRI10GE_ETH_STOPPED)
609510253Sxiuyan.wang@Sun.COM 		return (DDI_SUCCESS);
609610253Sxiuyan.wang@Sun.COM 
609710253Sxiuyan.wang@Sun.COM 	/* send a down CMD to queuesce NIC */
609810253Sxiuyan.wang@Sun.COM 	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
609910253Sxiuyan.wang@Sun.COM 	if (status) {
610010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "%s: Couldn't bring down link\n", mgp->name);
610110253Sxiuyan.wang@Sun.COM 		return (DDI_FAILURE);
610210253Sxiuyan.wang@Sun.COM 	}
610310253Sxiuyan.wang@Sun.COM 
610410253Sxiuyan.wang@Sun.COM 	for (i = 0; i < 20; i++) {
610510253Sxiuyan.wang@Sun.COM 		down = myri10ge_poll_down(mgp);
610610253Sxiuyan.wang@Sun.COM 		if (down)
610710253Sxiuyan.wang@Sun.COM 			break;
610810253Sxiuyan.wang@Sun.COM 		delay(drv_usectohz(100000));
610910253Sxiuyan.wang@Sun.COM 		mb();
611010253Sxiuyan.wang@Sun.COM 	}
611110253Sxiuyan.wang@Sun.COM 	if (down)
611210253Sxiuyan.wang@Sun.COM 		return (DDI_SUCCESS);
611310253Sxiuyan.wang@Sun.COM 	return (DDI_FAILURE);
611410253Sxiuyan.wang@Sun.COM }
611510253Sxiuyan.wang@Sun.COM 
611610253Sxiuyan.wang@Sun.COM /*
611710253Sxiuyan.wang@Sun.COM  * Distinguish between allocb'ed blocks, and gesballoc'ed attached
611810253Sxiuyan.wang@Sun.COM  * storage.
611910253Sxiuyan.wang@Sun.COM  */
612010253Sxiuyan.wang@Sun.COM static void
myri10ge_find_lastfree(void)612110253Sxiuyan.wang@Sun.COM myri10ge_find_lastfree(void)
612210253Sxiuyan.wang@Sun.COM {
612310253Sxiuyan.wang@Sun.COM 	mblk_t *mp = allocb(1024, 0);
612410253Sxiuyan.wang@Sun.COM 	dblk_t *dbp;
612510253Sxiuyan.wang@Sun.COM 
612610253Sxiuyan.wang@Sun.COM 	if (mp == NULL) {
612710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "myri10ge_find_lastfree failed\n");
612810253Sxiuyan.wang@Sun.COM 		return;
612910253Sxiuyan.wang@Sun.COM 	}
613010253Sxiuyan.wang@Sun.COM 	dbp = mp->b_datap;
613110253Sxiuyan.wang@Sun.COM 	myri10ge_db_lastfree = (void *)dbp->db_lastfree;
613210253Sxiuyan.wang@Sun.COM }
613310253Sxiuyan.wang@Sun.COM 
613410253Sxiuyan.wang@Sun.COM int
_init(void)613510253Sxiuyan.wang@Sun.COM _init(void)
613610253Sxiuyan.wang@Sun.COM {
613710253Sxiuyan.wang@Sun.COM 	int i;
613810253Sxiuyan.wang@Sun.COM 
613910253Sxiuyan.wang@Sun.COM 	if (myri10ge_verbose)
614010253Sxiuyan.wang@Sun.COM 		cmn_err(CE_NOTE,
614110253Sxiuyan.wang@Sun.COM 		    "Myricom 10G driver (10GbE) version %s loading\n",
614210253Sxiuyan.wang@Sun.COM 		    MYRI10GE_VERSION_STR);
614310253Sxiuyan.wang@Sun.COM 	myri10ge_find_lastfree();
614410253Sxiuyan.wang@Sun.COM 	mac_init_ops(&myri10ge_ops, "myri10ge");
614510253Sxiuyan.wang@Sun.COM 	mutex_init(&myri10ge_param_lock, NULL, MUTEX_DEFAULT, NULL);
614610253Sxiuyan.wang@Sun.COM 	if ((i = mod_install(&modlinkage)) != 0) {
614710253Sxiuyan.wang@Sun.COM 		cmn_err(CE_WARN, "mod_install returned %d\n", i);
614810253Sxiuyan.wang@Sun.COM 		mac_fini_ops(&myri10ge_ops);
614910253Sxiuyan.wang@Sun.COM 		mutex_destroy(&myri10ge_param_lock);
615010253Sxiuyan.wang@Sun.COM 	}
615110253Sxiuyan.wang@Sun.COM 	return (i);
615210253Sxiuyan.wang@Sun.COM }
615310253Sxiuyan.wang@Sun.COM 
615410253Sxiuyan.wang@Sun.COM int
_fini(void)615510253Sxiuyan.wang@Sun.COM _fini(void)
615610253Sxiuyan.wang@Sun.COM {
615710253Sxiuyan.wang@Sun.COM 	int i;
615810253Sxiuyan.wang@Sun.COM 	i = mod_remove(&modlinkage);
615910253Sxiuyan.wang@Sun.COM 	if (i != 0) {
616010253Sxiuyan.wang@Sun.COM 		return (i);
616110253Sxiuyan.wang@Sun.COM 	}
616210253Sxiuyan.wang@Sun.COM 	mac_fini_ops(&myri10ge_ops);
616310253Sxiuyan.wang@Sun.COM 	mutex_destroy(&myri10ge_param_lock);
616410253Sxiuyan.wang@Sun.COM 	return (0);
616510253Sxiuyan.wang@Sun.COM }
616610253Sxiuyan.wang@Sun.COM 
616710253Sxiuyan.wang@Sun.COM int
_info(struct modinfo * modinfop)616810253Sxiuyan.wang@Sun.COM _info(struct modinfo *modinfop)
616910253Sxiuyan.wang@Sun.COM {
617010253Sxiuyan.wang@Sun.COM 	return (mod_info(&modlinkage, modinfop));
617110253Sxiuyan.wang@Sun.COM }
617210253Sxiuyan.wang@Sun.COM 
617310253Sxiuyan.wang@Sun.COM 
617410253Sxiuyan.wang@Sun.COM /*
617510253Sxiuyan.wang@Sun.COM  *  This file uses MyriGE driver indentation.
617610253Sxiuyan.wang@Sun.COM  *
617710253Sxiuyan.wang@Sun.COM  * Local Variables:
617810253Sxiuyan.wang@Sun.COM  * c-file-style:"sun"
617910253Sxiuyan.wang@Sun.COM  * tab-width:8
618010253Sxiuyan.wang@Sun.COM  * End:
618110253Sxiuyan.wang@Sun.COM  */
6182