xref: /onnv-gate/usr/src/uts/common/io/yge/yge.c (revision 12390:6f9d114c81d8)
110506SBarry.Harding@Sun.COM /*
2*12390Sgdamore@opensolaris.org  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
310506SBarry.Harding@Sun.COM  */
410506SBarry.Harding@Sun.COM 
510506SBarry.Harding@Sun.COM /*
610506SBarry.Harding@Sun.COM  * This driver was derived from the FreeBSD if_msk.c driver, which
710506SBarry.Harding@Sun.COM  * bears the following copyright attributions and licenses.
810506SBarry.Harding@Sun.COM  */
910506SBarry.Harding@Sun.COM 
1010506SBarry.Harding@Sun.COM /*
1110506SBarry.Harding@Sun.COM  *
1210506SBarry.Harding@Sun.COM  *	LICENSE:
1310506SBarry.Harding@Sun.COM  *	Copyright (C) Marvell International Ltd. and/or its affiliates
1410506SBarry.Harding@Sun.COM  *
1510506SBarry.Harding@Sun.COM  *	The computer program files contained in this folder ("Files")
1610506SBarry.Harding@Sun.COM  *	are provided to you under the BSD-type license terms provided
1710506SBarry.Harding@Sun.COM  *	below, and any use of such Files and any derivative works
1810506SBarry.Harding@Sun.COM  *	thereof created by you shall be governed by the following terms
1910506SBarry.Harding@Sun.COM  *	and conditions:
2010506SBarry.Harding@Sun.COM  *
2110506SBarry.Harding@Sun.COM  *	- Redistributions of source code must retain the above copyright
2210506SBarry.Harding@Sun.COM  *	  notice, this list of conditions and the following disclaimer.
2310506SBarry.Harding@Sun.COM  *	- Redistributions in binary form must reproduce the above
2410506SBarry.Harding@Sun.COM  *	  copyright notice, this list of conditions and the following
2510506SBarry.Harding@Sun.COM  *	  disclaimer in the documentation and/or other materials provided
2610506SBarry.Harding@Sun.COM  *	  with the distribution.
2710506SBarry.Harding@Sun.COM  *	- Neither the name of Marvell nor the names of its contributors
2810506SBarry.Harding@Sun.COM  *	  may be used to endorse or promote products derived from this
2910506SBarry.Harding@Sun.COM  *	  software without specific prior written permission.
3010506SBarry.Harding@Sun.COM  *
3110506SBarry.Harding@Sun.COM  *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3210506SBarry.Harding@Sun.COM  *	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3310506SBarry.Harding@Sun.COM  *	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
3410506SBarry.Harding@Sun.COM  *	FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
3510506SBarry.Harding@Sun.COM  *	COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
3610506SBarry.Harding@Sun.COM  *	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
3710506SBarry.Harding@Sun.COM  *	BUT NOT LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR SERVICES;
3810506SBarry.Harding@Sun.COM  *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3910506SBarry.Harding@Sun.COM  *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4010506SBarry.Harding@Sun.COM  *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4110506SBarry.Harding@Sun.COM  *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
4210506SBarry.Harding@Sun.COM  *	OF THE POSSIBILITY OF SUCH DAMAGE.
4310506SBarry.Harding@Sun.COM  *	/LICENSE
4410506SBarry.Harding@Sun.COM  *
4510506SBarry.Harding@Sun.COM  */
4610506SBarry.Harding@Sun.COM /*
4710506SBarry.Harding@Sun.COM  * Copyright (c) 1997, 1998, 1999, 2000
4810506SBarry.Harding@Sun.COM  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4910506SBarry.Harding@Sun.COM  *
5010506SBarry.Harding@Sun.COM  * Redistribution and use in source and binary forms, with or without
5110506SBarry.Harding@Sun.COM  * modification, are permitted provided that the following conditions
5210506SBarry.Harding@Sun.COM  * are met:
5310506SBarry.Harding@Sun.COM  * 1. Redistributions of source code must retain the above copyright
5410506SBarry.Harding@Sun.COM  *    notice, this list of conditions and the following disclaimer.
5510506SBarry.Harding@Sun.COM  * 2. Redistributions in binary form must reproduce the above copyright
5610506SBarry.Harding@Sun.COM  *    notice, this list of conditions and the following disclaimer in the
5710506SBarry.Harding@Sun.COM  *    documentation and/or other materials provided with the distribution.
5810506SBarry.Harding@Sun.COM  * 3. All advertising materials mentioning features or use of this software
5910506SBarry.Harding@Sun.COM  *    must display the following acknowledgement:
6010506SBarry.Harding@Sun.COM  *	This product includes software developed by Bill Paul.
6110506SBarry.Harding@Sun.COM  * 4. Neither the name of the author nor the names of any co-contributors
6210506SBarry.Harding@Sun.COM  *    may be used to endorse or promote products derived from this software
6310506SBarry.Harding@Sun.COM  *    without specific prior written permission.
6410506SBarry.Harding@Sun.COM  *
6510506SBarry.Harding@Sun.COM  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
6610506SBarry.Harding@Sun.COM  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6710506SBarry.Harding@Sun.COM  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6810506SBarry.Harding@Sun.COM  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
6910506SBarry.Harding@Sun.COM  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7010506SBarry.Harding@Sun.COM  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7110506SBarry.Harding@Sun.COM  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
7210506SBarry.Harding@Sun.COM  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
7310506SBarry.Harding@Sun.COM  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
7410506SBarry.Harding@Sun.COM  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
7510506SBarry.Harding@Sun.COM  * THE POSSIBILITY OF SUCH DAMAGE.
7610506SBarry.Harding@Sun.COM  */
7710506SBarry.Harding@Sun.COM /*
7810506SBarry.Harding@Sun.COM  * Copyright (c) 2003 Nathan L. Binkert <binkertn@umich.edu>
7910506SBarry.Harding@Sun.COM  *
8010506SBarry.Harding@Sun.COM  * Permission to use, copy, modify, and distribute this software for any
8110506SBarry.Harding@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
8210506SBarry.Harding@Sun.COM  * copyright notice and this permission notice appear in all copies.
8310506SBarry.Harding@Sun.COM  *
8410506SBarry.Harding@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8510506SBarry.Harding@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
8610506SBarry.Harding@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
8710506SBarry.Harding@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
8810506SBarry.Harding@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
8910506SBarry.Harding@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
9010506SBarry.Harding@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
9110506SBarry.Harding@Sun.COM  */
9210506SBarry.Harding@Sun.COM 
9310506SBarry.Harding@Sun.COM #include <sys/varargs.h>
9410506SBarry.Harding@Sun.COM #include <sys/types.h>
9510506SBarry.Harding@Sun.COM #include <sys/modctl.h>
9610506SBarry.Harding@Sun.COM #include <sys/conf.h>
9710506SBarry.Harding@Sun.COM #include <sys/devops.h>
9810506SBarry.Harding@Sun.COM #include <sys/stream.h>
9910506SBarry.Harding@Sun.COM #include <sys/strsun.h>
10010506SBarry.Harding@Sun.COM #include <sys/cmn_err.h>
10110506SBarry.Harding@Sun.COM #include <sys/ethernet.h>
10210506SBarry.Harding@Sun.COM #include <sys/kmem.h>
10310506SBarry.Harding@Sun.COM #include <sys/time.h>
10410506SBarry.Harding@Sun.COM #include <sys/pci.h>
10510506SBarry.Harding@Sun.COM #include <sys/mii.h>
10610506SBarry.Harding@Sun.COM #include <sys/miiregs.h>
10710506SBarry.Harding@Sun.COM #include <sys/mac.h>
10810506SBarry.Harding@Sun.COM #include <sys/mac_ether.h>
10910506SBarry.Harding@Sun.COM #include <sys/mac_provider.h>
11010506SBarry.Harding@Sun.COM #include <sys/debug.h>
11110506SBarry.Harding@Sun.COM #include <sys/note.h>
11210506SBarry.Harding@Sun.COM #include <sys/ddi.h>
11310506SBarry.Harding@Sun.COM #include <sys/sunddi.h>
11410506SBarry.Harding@Sun.COM #include <sys/vlan.h>
11510506SBarry.Harding@Sun.COM 
11610506SBarry.Harding@Sun.COM #include "yge.h"
11710506SBarry.Harding@Sun.COM 
11810506SBarry.Harding@Sun.COM static struct ddi_device_acc_attr yge_regs_attr = {
11910506SBarry.Harding@Sun.COM 	DDI_DEVICE_ATTR_V0,
12010506SBarry.Harding@Sun.COM 	DDI_STRUCTURE_LE_ACC,
12111236SStephen.Hanson@Sun.COM 	DDI_STRICTORDER_ACC
12210506SBarry.Harding@Sun.COM };
12310506SBarry.Harding@Sun.COM 
12410506SBarry.Harding@Sun.COM static struct ddi_device_acc_attr yge_ring_attr = {
12510506SBarry.Harding@Sun.COM 	DDI_DEVICE_ATTR_V0,
12610506SBarry.Harding@Sun.COM 	DDI_STRUCTURE_LE_ACC,
12710506SBarry.Harding@Sun.COM 	DDI_STRICTORDER_ACC
12810506SBarry.Harding@Sun.COM };
12910506SBarry.Harding@Sun.COM 
13010506SBarry.Harding@Sun.COM static struct ddi_device_acc_attr yge_buf_attr = {
13110506SBarry.Harding@Sun.COM 	DDI_DEVICE_ATTR_V0,
13210506SBarry.Harding@Sun.COM 	DDI_NEVERSWAP_ACC,
13310506SBarry.Harding@Sun.COM 	DDI_STRICTORDER_ACC
13410506SBarry.Harding@Sun.COM };
13510506SBarry.Harding@Sun.COM 
13610506SBarry.Harding@Sun.COM #define	DESC_ALIGN	0x1000
13710506SBarry.Harding@Sun.COM 
13810506SBarry.Harding@Sun.COM static ddi_dma_attr_t yge_ring_dma_attr = {
13910506SBarry.Harding@Sun.COM 	DMA_ATTR_V0,		/* dma_attr_version */
14010506SBarry.Harding@Sun.COM 	0,			/* dma_attr_addr_lo */
14110506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_addr_hi */
14210506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_count_max */
14310506SBarry.Harding@Sun.COM 	DESC_ALIGN,		/* dma_attr_align */
14410506SBarry.Harding@Sun.COM 	0x000007fc,		/* dma_attr_burstsizes */
14510506SBarry.Harding@Sun.COM 	1,			/* dma_attr_minxfer */
14610506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_maxxfer */
14710506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_seg */
14810506SBarry.Harding@Sun.COM 	1,			/* dma_attr_sgllen */
14910506SBarry.Harding@Sun.COM 	1,			/* dma_attr_granular */
15010506SBarry.Harding@Sun.COM 	0			/* dma_attr_flags */
15110506SBarry.Harding@Sun.COM };
15210506SBarry.Harding@Sun.COM 
15310506SBarry.Harding@Sun.COM static ddi_dma_attr_t yge_buf_dma_attr = {
15410506SBarry.Harding@Sun.COM 	DMA_ATTR_V0,		/* dma_attr_version */
15510506SBarry.Harding@Sun.COM 	0,			/* dma_attr_addr_lo */
15610506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_addr_hi */
15710506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_count_max */
15810506SBarry.Harding@Sun.COM 	1,			/* dma_attr_align */
15910506SBarry.Harding@Sun.COM 	0x0000fffc,		/* dma_attr_burstsizes */
16010506SBarry.Harding@Sun.COM 	1,			/* dma_attr_minxfer */
16110506SBarry.Harding@Sun.COM 	0x000000000000ffffull,	/* dma_attr_maxxfer */
16210506SBarry.Harding@Sun.COM 	0x00000000ffffffffull,	/* dma_attr_seg */
16310506SBarry.Harding@Sun.COM 	8,			/* dma_attr_sgllen */
16410506SBarry.Harding@Sun.COM 	1,			/* dma_attr_granular */
16510506SBarry.Harding@Sun.COM 	0			/* dma_attr_flags */
16610506SBarry.Harding@Sun.COM };
16710506SBarry.Harding@Sun.COM 
16810506SBarry.Harding@Sun.COM 
16910506SBarry.Harding@Sun.COM static int yge_attach(yge_dev_t *);
17010506SBarry.Harding@Sun.COM static void yge_detach(yge_dev_t *);
17110506SBarry.Harding@Sun.COM static int yge_suspend(yge_dev_t *);
17210506SBarry.Harding@Sun.COM static int yge_resume(yge_dev_t *);
17310506SBarry.Harding@Sun.COM 
17410506SBarry.Harding@Sun.COM static void yge_reset(yge_dev_t *);
17510506SBarry.Harding@Sun.COM static void yge_setup_rambuffer(yge_dev_t *);
17610506SBarry.Harding@Sun.COM 
17710506SBarry.Harding@Sun.COM static int yge_init_port(yge_port_t *);
17810506SBarry.Harding@Sun.COM static void yge_uninit_port(yge_port_t *);
17910506SBarry.Harding@Sun.COM static int yge_register_port(yge_port_t *);
18010506SBarry.Harding@Sun.COM 
18110506SBarry.Harding@Sun.COM static void yge_tick(void *);
18210506SBarry.Harding@Sun.COM static uint_t yge_intr(caddr_t, caddr_t);
18310506SBarry.Harding@Sun.COM static int yge_intr_gmac(yge_port_t *);
18410506SBarry.Harding@Sun.COM static void yge_intr_enable(yge_dev_t *);
18510506SBarry.Harding@Sun.COM static void yge_intr_disable(yge_dev_t *);
18610506SBarry.Harding@Sun.COM static boolean_t yge_handle_events(yge_dev_t *, mblk_t **, mblk_t **, int *);
18710506SBarry.Harding@Sun.COM static void yge_handle_hwerr(yge_port_t *, uint32_t);
18810506SBarry.Harding@Sun.COM static void yge_intr_hwerr(yge_dev_t *);
18910506SBarry.Harding@Sun.COM static mblk_t *yge_rxeof(yge_port_t *, uint32_t, int);
19010506SBarry.Harding@Sun.COM static void yge_txeof(yge_port_t *, int);
19110506SBarry.Harding@Sun.COM static boolean_t yge_send(yge_port_t *, mblk_t *);
19210506SBarry.Harding@Sun.COM static void yge_set_prefetch(yge_dev_t *, int, yge_ring_t *);
19310506SBarry.Harding@Sun.COM static void yge_set_rambuffer(yge_port_t *);
19410506SBarry.Harding@Sun.COM static void yge_start_port(yge_port_t *);
19510506SBarry.Harding@Sun.COM static void yge_stop_port(yge_port_t *);
19610506SBarry.Harding@Sun.COM static void yge_phy_power(yge_dev_t *, boolean_t);
19710506SBarry.Harding@Sun.COM static int yge_alloc_ring(yge_port_t *, yge_dev_t *, yge_ring_t *, uint32_t);
19810506SBarry.Harding@Sun.COM static void yge_free_ring(yge_ring_t *);
19910506SBarry.Harding@Sun.COM static uint8_t yge_find_capability(yge_dev_t *, uint8_t);
20010506SBarry.Harding@Sun.COM 
20110506SBarry.Harding@Sun.COM static int yge_txrx_dma_alloc(yge_port_t *);
20210506SBarry.Harding@Sun.COM static void yge_txrx_dma_free(yge_port_t *);
20310506SBarry.Harding@Sun.COM static void yge_init_rx_ring(yge_port_t *);
20410506SBarry.Harding@Sun.COM static void yge_init_tx_ring(yge_port_t *);
20510506SBarry.Harding@Sun.COM 
20610506SBarry.Harding@Sun.COM static uint16_t yge_mii_readreg(yge_port_t *, uint8_t, uint8_t);
20710506SBarry.Harding@Sun.COM static void yge_mii_writereg(yge_port_t *, uint8_t, uint8_t, uint16_t);
20810506SBarry.Harding@Sun.COM 
20910506SBarry.Harding@Sun.COM static uint16_t yge_mii_read(void *, uint8_t, uint8_t);
21010506SBarry.Harding@Sun.COM static void yge_mii_write(void *, uint8_t, uint8_t, uint16_t);
21110506SBarry.Harding@Sun.COM static void yge_mii_notify(void *, link_state_t);
21210506SBarry.Harding@Sun.COM 
21310506SBarry.Harding@Sun.COM static void yge_setrxfilt(yge_port_t *);
21410506SBarry.Harding@Sun.COM static void yge_restart_task(yge_dev_t *);
21510506SBarry.Harding@Sun.COM static void yge_task(void *);
21610506SBarry.Harding@Sun.COM static void yge_dispatch(yge_dev_t *, int);
21710506SBarry.Harding@Sun.COM 
21810506SBarry.Harding@Sun.COM static void yge_stats_clear(yge_port_t *);
21910506SBarry.Harding@Sun.COM static void yge_stats_update(yge_port_t *);
22010506SBarry.Harding@Sun.COM static uint32_t yge_hashbit(const uint8_t *);
22110506SBarry.Harding@Sun.COM 
22210506SBarry.Harding@Sun.COM static int yge_m_unicst(void *, const uint8_t *);
22310506SBarry.Harding@Sun.COM static int yge_m_multicst(void *, boolean_t, const uint8_t *);
22410506SBarry.Harding@Sun.COM static int yge_m_promisc(void *, boolean_t);
22510506SBarry.Harding@Sun.COM static mblk_t *yge_m_tx(void *, mblk_t *);
22610506SBarry.Harding@Sun.COM static int yge_m_stat(void *, uint_t, uint64_t *);
22710506SBarry.Harding@Sun.COM static int yge_m_start(void *);
22810506SBarry.Harding@Sun.COM static void yge_m_stop(void *);
22911878SVenu.Iyer@Sun.COM static int yge_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
23011878SVenu.Iyer@Sun.COM static void yge_m_propinfo(void *, const char *, mac_prop_id_t,
23111878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
23210506SBarry.Harding@Sun.COM static int yge_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
23310506SBarry.Harding@Sun.COM     const void *);
23410506SBarry.Harding@Sun.COM static void yge_m_ioctl(void *, queue_t *, mblk_t *);
23510506SBarry.Harding@Sun.COM 
23610506SBarry.Harding@Sun.COM void yge_error(yge_dev_t *, yge_port_t *, char *, ...);
23710506SBarry.Harding@Sun.COM extern void yge_phys_update(yge_port_t *);
23810506SBarry.Harding@Sun.COM extern int yge_phys_restart(yge_port_t *, boolean_t);
23910506SBarry.Harding@Sun.COM extern int yge_phys_init(yge_port_t *, phy_readreg_t, phy_writereg_t);
24010506SBarry.Harding@Sun.COM 
24110506SBarry.Harding@Sun.COM static mac_callbacks_t yge_m_callbacks = {
24211878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
24310506SBarry.Harding@Sun.COM 	yge_m_stat,
24410506SBarry.Harding@Sun.COM 	yge_m_start,
24510506SBarry.Harding@Sun.COM 	yge_m_stop,
24610506SBarry.Harding@Sun.COM 	yge_m_promisc,
24710506SBarry.Harding@Sun.COM 	yge_m_multicst,
24810506SBarry.Harding@Sun.COM 	yge_m_unicst,
24910506SBarry.Harding@Sun.COM 	yge_m_tx,
25011878SVenu.Iyer@Sun.COM 	NULL,
25110506SBarry.Harding@Sun.COM 	yge_m_ioctl,
25210506SBarry.Harding@Sun.COM 	NULL,		/* mc_getcapab */
25310506SBarry.Harding@Sun.COM 	NULL,		/* mc_open */
25410506SBarry.Harding@Sun.COM 	NULL,		/* mc_close */
25510506SBarry.Harding@Sun.COM 	yge_m_setprop,
25610506SBarry.Harding@Sun.COM 	yge_m_getprop,
25711878SVenu.Iyer@Sun.COM 	yge_m_propinfo
25810506SBarry.Harding@Sun.COM };
25910506SBarry.Harding@Sun.COM 
26010506SBarry.Harding@Sun.COM static mii_ops_t yge_mii_ops = {
26110506SBarry.Harding@Sun.COM 	MII_OPS_VERSION,
26210506SBarry.Harding@Sun.COM 	yge_mii_read,
26310506SBarry.Harding@Sun.COM 	yge_mii_write,
26410506SBarry.Harding@Sun.COM 	yge_mii_notify,
26510506SBarry.Harding@Sun.COM 	NULL	/* reset */
26610506SBarry.Harding@Sun.COM };
26710506SBarry.Harding@Sun.COM 
26810506SBarry.Harding@Sun.COM /*
26910506SBarry.Harding@Sun.COM  * This is the low level interface routine to read from the PHY
27010506SBarry.Harding@Sun.COM  * MII registers. There is multiple steps to these accesses. First
27110506SBarry.Harding@Sun.COM  * the register number is written to an address register. Then after
27210506SBarry.Harding@Sun.COM  * a specified delay status is checked until the data is present.
27310506SBarry.Harding@Sun.COM  */
27410506SBarry.Harding@Sun.COM static uint16_t
yge_mii_readreg(yge_port_t * port,uint8_t phy,uint8_t reg)27510506SBarry.Harding@Sun.COM yge_mii_readreg(yge_port_t *port, uint8_t phy, uint8_t reg)
27610506SBarry.Harding@Sun.COM {
27710506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
27810506SBarry.Harding@Sun.COM 	int pnum = port->p_port;
27910506SBarry.Harding@Sun.COM 	uint16_t val;
28010506SBarry.Harding@Sun.COM 
28110506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_SMI_CTRL,
28210506SBarry.Harding@Sun.COM 	    GM_SMI_CT_PHY_AD(phy) | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
28310506SBarry.Harding@Sun.COM 
28410506SBarry.Harding@Sun.COM 	for (int i = 0; i < YGE_TIMEOUT; i += 10) {
28510506SBarry.Harding@Sun.COM 		drv_usecwait(10);
28610506SBarry.Harding@Sun.COM 		val = GMAC_READ_2(dev, pnum, GM_SMI_CTRL);
28710506SBarry.Harding@Sun.COM 		if ((val & GM_SMI_CT_RD_VAL) != 0) {
28810506SBarry.Harding@Sun.COM 			val = GMAC_READ_2(dev, pnum, GM_SMI_DATA);
28910506SBarry.Harding@Sun.COM 			return (val);
29010506SBarry.Harding@Sun.COM 		}
29110506SBarry.Harding@Sun.COM 	}
29210506SBarry.Harding@Sun.COM 
29310506SBarry.Harding@Sun.COM 	return (0xffff);
29410506SBarry.Harding@Sun.COM }
29510506SBarry.Harding@Sun.COM 
29610506SBarry.Harding@Sun.COM /*
29710506SBarry.Harding@Sun.COM  * This is the low level interface routine to write to the PHY
29810506SBarry.Harding@Sun.COM  * MII registers. There is multiple steps to these accesses. The
29910506SBarry.Harding@Sun.COM  * data and the target registers address are written to the PHY.
30010506SBarry.Harding@Sun.COM  * Then the PHY is polled until it is done with the write. Note
30110506SBarry.Harding@Sun.COM  * that the delays are specified and required!
30210506SBarry.Harding@Sun.COM  */
30310506SBarry.Harding@Sun.COM static void
yge_mii_writereg(yge_port_t * port,uint8_t phy,uint8_t reg,uint16_t val)30410506SBarry.Harding@Sun.COM yge_mii_writereg(yge_port_t *port, uint8_t phy, uint8_t reg, uint16_t val)
30510506SBarry.Harding@Sun.COM {
30610506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
30710506SBarry.Harding@Sun.COM 	int pnum = port->p_port;
30810506SBarry.Harding@Sun.COM 
30910506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_SMI_DATA, val);
31010506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_SMI_CTRL,
31110506SBarry.Harding@Sun.COM 	    GM_SMI_CT_PHY_AD(phy) | GM_SMI_CT_REG_AD(reg));
31210506SBarry.Harding@Sun.COM 
31310506SBarry.Harding@Sun.COM 	for (int i = 0; i < YGE_TIMEOUT; i += 10) {
31410506SBarry.Harding@Sun.COM 		drv_usecwait(10);
31510506SBarry.Harding@Sun.COM 		if ((GMAC_READ_2(dev, pnum, GM_SMI_CTRL) & GM_SMI_CT_BUSY) == 0)
31610506SBarry.Harding@Sun.COM 			return;
31710506SBarry.Harding@Sun.COM 	}
31810506SBarry.Harding@Sun.COM 
31910506SBarry.Harding@Sun.COM 	yge_error(NULL, port, "phy write timeout");
32010506SBarry.Harding@Sun.COM }
32110506SBarry.Harding@Sun.COM 
32210506SBarry.Harding@Sun.COM static uint16_t
yge_mii_read(void * arg,uint8_t phy,uint8_t reg)32310506SBarry.Harding@Sun.COM yge_mii_read(void *arg, uint8_t phy, uint8_t reg)
32410506SBarry.Harding@Sun.COM {
32510506SBarry.Harding@Sun.COM 	yge_port_t *port = arg;
32610506SBarry.Harding@Sun.COM 	uint16_t rv;
32710506SBarry.Harding@Sun.COM 
32810506SBarry.Harding@Sun.COM 	PHY_LOCK(port->p_dev);
32910506SBarry.Harding@Sun.COM 	rv = yge_mii_readreg(port, phy, reg);
33010506SBarry.Harding@Sun.COM 	PHY_UNLOCK(port->p_dev);
33110506SBarry.Harding@Sun.COM 	return (rv);
33210506SBarry.Harding@Sun.COM }
33310506SBarry.Harding@Sun.COM 
33410506SBarry.Harding@Sun.COM static void
yge_mii_write(void * arg,uint8_t phy,uint8_t reg,uint16_t val)33510506SBarry.Harding@Sun.COM yge_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
33610506SBarry.Harding@Sun.COM {
33710506SBarry.Harding@Sun.COM 	yge_port_t *port = arg;
33810506SBarry.Harding@Sun.COM 
33910506SBarry.Harding@Sun.COM 	PHY_LOCK(port->p_dev);
34010506SBarry.Harding@Sun.COM 	yge_mii_writereg(port, phy, reg, val);
34110506SBarry.Harding@Sun.COM 	PHY_UNLOCK(port->p_dev);
34210506SBarry.Harding@Sun.COM }
34310506SBarry.Harding@Sun.COM 
34410506SBarry.Harding@Sun.COM /*
34510506SBarry.Harding@Sun.COM  * The MII common code calls this function to let the MAC driver
34610506SBarry.Harding@Sun.COM  * know when there has been a change in status.
34710506SBarry.Harding@Sun.COM  */
34810506SBarry.Harding@Sun.COM void
yge_mii_notify(void * arg,link_state_t link)34910506SBarry.Harding@Sun.COM yge_mii_notify(void *arg, link_state_t link)
35010506SBarry.Harding@Sun.COM {
35110506SBarry.Harding@Sun.COM 	yge_port_t *port = arg;
35210506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
35310506SBarry.Harding@Sun.COM 	uint32_t gmac;
35410506SBarry.Harding@Sun.COM 	uint32_t gpcr;
35510506SBarry.Harding@Sun.COM 	link_flowctrl_t	fc;
35610506SBarry.Harding@Sun.COM 	link_duplex_t duplex;
35710506SBarry.Harding@Sun.COM 	int speed;
35810506SBarry.Harding@Sun.COM 
35910506SBarry.Harding@Sun.COM 	fc = mii_get_flowctrl(port->p_mii);
36010506SBarry.Harding@Sun.COM 	duplex = mii_get_duplex(port->p_mii);
36110506SBarry.Harding@Sun.COM 	speed = mii_get_speed(port->p_mii);
36210506SBarry.Harding@Sun.COM 
36310506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
36410506SBarry.Harding@Sun.COM 
36510506SBarry.Harding@Sun.COM 	if (link == LINK_STATE_UP) {
36610506SBarry.Harding@Sun.COM 
36710506SBarry.Harding@Sun.COM 		/* Enable Tx FIFO Underrun. */
36810506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, MR_ADDR(port->p_port, GMAC_IRQ_MSK),
36910506SBarry.Harding@Sun.COM 		    GM_IS_TX_FF_UR |	/* TX FIFO underflow */
37010506SBarry.Harding@Sun.COM 		    GM_IS_RX_FF_OR);	/* RX FIFO overflow */
37110506SBarry.Harding@Sun.COM 
37210506SBarry.Harding@Sun.COM 		gpcr = GM_GPCR_AU_ALL_DIS;
37310506SBarry.Harding@Sun.COM 
37410506SBarry.Harding@Sun.COM 		switch (fc) {
37510506SBarry.Harding@Sun.COM 		case LINK_FLOWCTRL_BI:
37610506SBarry.Harding@Sun.COM 			gmac = GMC_PAUSE_ON;
37710506SBarry.Harding@Sun.COM 			gpcr &= ~(GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS);
37810506SBarry.Harding@Sun.COM 			break;
37910506SBarry.Harding@Sun.COM 		case LINK_FLOWCTRL_TX:
38010506SBarry.Harding@Sun.COM 			gmac = GMC_PAUSE_ON;
38110506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_RX_DIS;
38210506SBarry.Harding@Sun.COM 			break;
38310506SBarry.Harding@Sun.COM 		case LINK_FLOWCTRL_RX:
38410506SBarry.Harding@Sun.COM 			gmac = GMC_PAUSE_ON;
38510506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_TX_DIS;
38610506SBarry.Harding@Sun.COM 			break;
38710506SBarry.Harding@Sun.COM 		case LINK_FLOWCTRL_NONE:
38810506SBarry.Harding@Sun.COM 		default:
38910506SBarry.Harding@Sun.COM 			gmac = GMC_PAUSE_OFF;
39010506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_RX_DIS;
39110506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_TX_DIS;
39210506SBarry.Harding@Sun.COM 			break;
39310506SBarry.Harding@Sun.COM 		}
39410506SBarry.Harding@Sun.COM 
39510506SBarry.Harding@Sun.COM 		gpcr &= ~((GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100));
39610506SBarry.Harding@Sun.COM 		switch (speed) {
39710506SBarry.Harding@Sun.COM 		case 1000:
39810506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_SPEED_1000;
39910506SBarry.Harding@Sun.COM 			break;
40010506SBarry.Harding@Sun.COM 		case 100:
40110506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_SPEED_100;
40210506SBarry.Harding@Sun.COM 			break;
40310506SBarry.Harding@Sun.COM 		case 10:
40410506SBarry.Harding@Sun.COM 		default:
40510506SBarry.Harding@Sun.COM 			break;
40610506SBarry.Harding@Sun.COM 		}
40710506SBarry.Harding@Sun.COM 
40810506SBarry.Harding@Sun.COM 		if (duplex == LINK_DUPLEX_FULL) {
40910506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_DUP_FULL;
41010506SBarry.Harding@Sun.COM 		} else {
41110506SBarry.Harding@Sun.COM 			gpcr &= ~(GM_GPCR_DUP_FULL);
41210506SBarry.Harding@Sun.COM 			gmac = GMC_PAUSE_OFF;
41310506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_RX_DIS;
41410506SBarry.Harding@Sun.COM 			gpcr |= GM_GPCR_FC_TX_DIS;
41510506SBarry.Harding@Sun.COM 		}
41610506SBarry.Harding@Sun.COM 
41710506SBarry.Harding@Sun.COM 		gpcr |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
41810506SBarry.Harding@Sun.COM 		GMAC_WRITE_2(dev, port->p_port, GM_GP_CTRL, gpcr);
41910506SBarry.Harding@Sun.COM 
42010506SBarry.Harding@Sun.COM 		/* Read again to ensure writing. */
42110506SBarry.Harding@Sun.COM 		(void) GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
42210506SBarry.Harding@Sun.COM 
42310506SBarry.Harding@Sun.COM 		/* write out the flow control gmac setting */
42410506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(port->p_port, GMAC_CTRL), gmac);
42510506SBarry.Harding@Sun.COM 
42610506SBarry.Harding@Sun.COM 	} else {
42710506SBarry.Harding@Sun.COM 		/* Disable Rx/Tx MAC. */
42810506SBarry.Harding@Sun.COM 		gpcr = GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
42910506SBarry.Harding@Sun.COM 		gpcr &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
43010506SBarry.Harding@Sun.COM 		GMAC_WRITE_2(dev, port->p_port, GM_GP_CTRL, gpcr);
43110506SBarry.Harding@Sun.COM 
43210506SBarry.Harding@Sun.COM 		/* Read again to ensure writing. */
43310506SBarry.Harding@Sun.COM 		(void) GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
43410506SBarry.Harding@Sun.COM 	}
43510506SBarry.Harding@Sun.COM 
43610506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
43710506SBarry.Harding@Sun.COM 
43810506SBarry.Harding@Sun.COM 	mac_link_update(port->p_mh, link);
43910506SBarry.Harding@Sun.COM 
44010506SBarry.Harding@Sun.COM 	if (port->p_running && (link == LINK_STATE_UP)) {
44110506SBarry.Harding@Sun.COM 		mac_tx_update(port->p_mh);
44210506SBarry.Harding@Sun.COM 	}
44310506SBarry.Harding@Sun.COM }
44410506SBarry.Harding@Sun.COM 
44510506SBarry.Harding@Sun.COM static void
yge_setrxfilt(yge_port_t * port)44610506SBarry.Harding@Sun.COM yge_setrxfilt(yge_port_t *port)
44710506SBarry.Harding@Sun.COM {
44810506SBarry.Harding@Sun.COM 	yge_dev_t	*dev;
44910506SBarry.Harding@Sun.COM 	uint16_t	mode;
45010506SBarry.Harding@Sun.COM 	uint8_t		*ea;
45110506SBarry.Harding@Sun.COM 	uint32_t	*mchash;
45210506SBarry.Harding@Sun.COM 	int		pnum;
45310506SBarry.Harding@Sun.COM 
45410506SBarry.Harding@Sun.COM 	dev = port->p_dev;
45510506SBarry.Harding@Sun.COM 	pnum = port->p_port;
45610506SBarry.Harding@Sun.COM 	ea = port->p_curraddr;
45710506SBarry.Harding@Sun.COM 	mchash = port->p_mchash;
45810506SBarry.Harding@Sun.COM 
45910506SBarry.Harding@Sun.COM 	if (dev->d_suspended)
46010506SBarry.Harding@Sun.COM 		return;
46110506SBarry.Harding@Sun.COM 
46210506SBarry.Harding@Sun.COM 	/* Set station address. */
46310506SBarry.Harding@Sun.COM 	for (int i = 0; i < (ETHERADDRL / 2); i++) {
46410506SBarry.Harding@Sun.COM 		GMAC_WRITE_2(dev, pnum, GM_SRC_ADDR_1L + i * 4,
46510506SBarry.Harding@Sun.COM 		    ((uint16_t)ea[i * 2] | ((uint16_t)ea[(i * 2) + 1] << 8)));
46610506SBarry.Harding@Sun.COM 	}
46710506SBarry.Harding@Sun.COM 	for (int i = 0; i < (ETHERADDRL / 2); i++) {
46810506SBarry.Harding@Sun.COM 		GMAC_WRITE_2(dev, pnum, GM_SRC_ADDR_2L + i * 4,
46910506SBarry.Harding@Sun.COM 		    ((uint16_t)ea[i * 2] | ((uint16_t)ea[(i * 2) + 1] << 8)));
47010506SBarry.Harding@Sun.COM 	}
47110506SBarry.Harding@Sun.COM 
47210506SBarry.Harding@Sun.COM 	/* Figure out receive filtering mode. */
47310506SBarry.Harding@Sun.COM 	mode = GMAC_READ_2(dev, pnum, GM_RX_CTRL);
47410506SBarry.Harding@Sun.COM 	if (port->p_promisc) {
47510506SBarry.Harding@Sun.COM 		mode &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
47610506SBarry.Harding@Sun.COM 	} else {
47710506SBarry.Harding@Sun.COM 		mode |= (GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
47810506SBarry.Harding@Sun.COM 	}
47910506SBarry.Harding@Sun.COM 	/* Write the multicast filter. */
48010506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H1, mchash[0] & 0xffff);
48110506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H2, (mchash[0] >> 16) & 0xffff);
48210506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H3, mchash[1] & 0xffff);
48310506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H4, (mchash[1] >> 16) & 0xffff);
48410506SBarry.Harding@Sun.COM 	/* Write the receive filtering mode. */
48510506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_RX_CTRL, mode);
48610506SBarry.Harding@Sun.COM }
48710506SBarry.Harding@Sun.COM 
48810506SBarry.Harding@Sun.COM static void
yge_init_rx_ring(yge_port_t * port)48910506SBarry.Harding@Sun.COM yge_init_rx_ring(yge_port_t *port)
49010506SBarry.Harding@Sun.COM {
49110506SBarry.Harding@Sun.COM 	yge_buf_t *rxb;
49210506SBarry.Harding@Sun.COM 	yge_ring_t *ring;
49310506SBarry.Harding@Sun.COM 	int prod;
49410506SBarry.Harding@Sun.COM 
49510506SBarry.Harding@Sun.COM 	port->p_rx_cons = 0;
49610506SBarry.Harding@Sun.COM 	port->p_rx_putwm = YGE_PUT_WM;
49710506SBarry.Harding@Sun.COM 	ring = &port->p_rx_ring;
49810506SBarry.Harding@Sun.COM 
49910506SBarry.Harding@Sun.COM 	/* ala bzero, but uses safer acch access */
50010506SBarry.Harding@Sun.COM 	CLEARRING(ring);
50110506SBarry.Harding@Sun.COM 
50210506SBarry.Harding@Sun.COM 	for (prod = 0; prod < YGE_RX_RING_CNT; prod++) {
50310506SBarry.Harding@Sun.COM 		/* Hang out receive buffers. */
50410506SBarry.Harding@Sun.COM 		rxb = &port->p_rx_buf[prod];
50510506SBarry.Harding@Sun.COM 
50610506SBarry.Harding@Sun.COM 		PUTADDR(ring, prod, rxb->b_paddr);
50710506SBarry.Harding@Sun.COM 		PUTCTRL(ring, prod, port->p_framesize | OP_PACKET | HW_OWNER);
50810506SBarry.Harding@Sun.COM 	}
50910506SBarry.Harding@Sun.COM 
51010506SBarry.Harding@Sun.COM 	SYNCRING(ring, DDI_DMA_SYNC_FORDEV);
51110506SBarry.Harding@Sun.COM 
51210506SBarry.Harding@Sun.COM 	yge_set_prefetch(port->p_dev, port->p_rxq, ring);
51310506SBarry.Harding@Sun.COM 
51410506SBarry.Harding@Sun.COM 	/* Update prefetch unit. */
51510506SBarry.Harding@Sun.COM 	CSR_WRITE_2(port->p_dev,
51610506SBarry.Harding@Sun.COM 	    Y2_PREF_Q_ADDR(port->p_rxq, PREF_UNIT_PUT_IDX_REG),
51710506SBarry.Harding@Sun.COM 	    YGE_RX_RING_CNT - 1);
51810506SBarry.Harding@Sun.COM }
51910506SBarry.Harding@Sun.COM 
52010506SBarry.Harding@Sun.COM static void
yge_init_tx_ring(yge_port_t * port)52110506SBarry.Harding@Sun.COM yge_init_tx_ring(yge_port_t *port)
52210506SBarry.Harding@Sun.COM {
52310506SBarry.Harding@Sun.COM 	yge_ring_t *ring = &port->p_tx_ring;
52410506SBarry.Harding@Sun.COM 
52510506SBarry.Harding@Sun.COM 	port->p_tx_prod = 0;
52610506SBarry.Harding@Sun.COM 	port->p_tx_cons = 0;
52710506SBarry.Harding@Sun.COM 	port->p_tx_cnt = 0;
52810506SBarry.Harding@Sun.COM 
52910506SBarry.Harding@Sun.COM 	CLEARRING(ring);
53010506SBarry.Harding@Sun.COM 	SYNCRING(ring, DDI_DMA_SYNC_FORDEV);
53110506SBarry.Harding@Sun.COM 
53210506SBarry.Harding@Sun.COM 	yge_set_prefetch(port->p_dev, port->p_txq, ring);
53310506SBarry.Harding@Sun.COM }
53410506SBarry.Harding@Sun.COM 
53510506SBarry.Harding@Sun.COM static void
yge_setup_rambuffer(yge_dev_t * dev)53610506SBarry.Harding@Sun.COM yge_setup_rambuffer(yge_dev_t *dev)
53710506SBarry.Harding@Sun.COM {
53810506SBarry.Harding@Sun.COM 	int next;
53910506SBarry.Harding@Sun.COM 	int i;
54010506SBarry.Harding@Sun.COM 
54110506SBarry.Harding@Sun.COM 	/* Get adapter SRAM size. */
54210506SBarry.Harding@Sun.COM 	dev->d_ramsize = CSR_READ_1(dev, B2_E_0) * 4;
54310506SBarry.Harding@Sun.COM 	if (dev->d_ramsize == 0)
54410506SBarry.Harding@Sun.COM 		return;
54510506SBarry.Harding@Sun.COM 
54610506SBarry.Harding@Sun.COM 	dev->d_pflags |= PORT_FLAG_RAMBUF;
54710506SBarry.Harding@Sun.COM 	/*
54810506SBarry.Harding@Sun.COM 	 * Give receiver 2/3 of memory and round down to the multiple
54910506SBarry.Harding@Sun.COM 	 * of 1024. Tx/Rx RAM buffer size of Yukon 2 should be multiple
55010506SBarry.Harding@Sun.COM 	 * of 1024.
55110506SBarry.Harding@Sun.COM 	 */
55210506SBarry.Harding@Sun.COM 	dev->d_rxqsize = (((dev->d_ramsize * 1024 * 2) / 3) & ~(1024 - 1));
55310506SBarry.Harding@Sun.COM 	dev->d_txqsize = (dev->d_ramsize * 1024) - dev->d_rxqsize;
55410506SBarry.Harding@Sun.COM 
55510506SBarry.Harding@Sun.COM 	for (i = 0, next = 0; i < dev->d_num_port; i++) {
55610506SBarry.Harding@Sun.COM 		dev->d_rxqstart[i] = next;
55710506SBarry.Harding@Sun.COM 		dev->d_rxqend[i] = next + dev->d_rxqsize - 1;
55810506SBarry.Harding@Sun.COM 		next = dev->d_rxqend[i] + 1;
55910506SBarry.Harding@Sun.COM 		dev->d_txqstart[i] = next;
56010506SBarry.Harding@Sun.COM 		dev->d_txqend[i] = next + dev->d_txqsize - 1;
56110506SBarry.Harding@Sun.COM 		next = dev->d_txqend[i] + 1;
56210506SBarry.Harding@Sun.COM 	}
56310506SBarry.Harding@Sun.COM }
56410506SBarry.Harding@Sun.COM 
56510506SBarry.Harding@Sun.COM static void
yge_phy_power(yge_dev_t * dev,boolean_t powerup)56610506SBarry.Harding@Sun.COM yge_phy_power(yge_dev_t *dev, boolean_t powerup)
56710506SBarry.Harding@Sun.COM {
56810506SBarry.Harding@Sun.COM 	uint32_t val;
56910506SBarry.Harding@Sun.COM 	int i;
57010506SBarry.Harding@Sun.COM 
57110506SBarry.Harding@Sun.COM 	if (powerup) {
57210506SBarry.Harding@Sun.COM 		/* Switch power to VCC (WA for VAUX problem). */
57310506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B0_POWER_CTRL,
57410506SBarry.Harding@Sun.COM 		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
57510506SBarry.Harding@Sun.COM 		/* Disable Core Clock Division, set Clock Select to 0. */
57610506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS);
57710506SBarry.Harding@Sun.COM 
57810506SBarry.Harding@Sun.COM 		val = 0;
57910506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
58010506SBarry.Harding@Sun.COM 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
58110506SBarry.Harding@Sun.COM 			/* Enable bits are inverted. */
58210506SBarry.Harding@Sun.COM 			val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
58310506SBarry.Harding@Sun.COM 			    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
58410506SBarry.Harding@Sun.COM 			    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS;
58510506SBarry.Harding@Sun.COM 		}
58610506SBarry.Harding@Sun.COM 		/*
58710506SBarry.Harding@Sun.COM 		 * Enable PCI & Core Clock, enable clock gating for both Links.
58810506SBarry.Harding@Sun.COM 		 */
58910506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_Y2_CLK_GATE, val);
59010506SBarry.Harding@Sun.COM 
59110506SBarry.Harding@Sun.COM 		val = pci_config_get32(dev->d_pcih, PCI_OUR_REG_1);
59210506SBarry.Harding@Sun.COM 		val &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
59310506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
59410506SBarry.Harding@Sun.COM 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
59510506SBarry.Harding@Sun.COM 			/* Deassert Low Power for 1st PHY. */
59610506SBarry.Harding@Sun.COM 			val |= PCI_Y2_PHY1_COMA;
59710506SBarry.Harding@Sun.COM 			if (dev->d_num_port > 1)
59810506SBarry.Harding@Sun.COM 				val |= PCI_Y2_PHY2_COMA;
59910506SBarry.Harding@Sun.COM 		}
60010506SBarry.Harding@Sun.COM 
60110506SBarry.Harding@Sun.COM 		/* Release PHY from PowerDown/COMA mode. */
60210506SBarry.Harding@Sun.COM 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, val);
60310506SBarry.Harding@Sun.COM 
60410506SBarry.Harding@Sun.COM 		switch (dev->d_hw_id) {
60510506SBarry.Harding@Sun.COM 		case CHIP_ID_YUKON_EC_U:
60610506SBarry.Harding@Sun.COM 		case CHIP_ID_YUKON_EX:
60710506SBarry.Harding@Sun.COM 		case CHIP_ID_YUKON_FE_P: {
60810506SBarry.Harding@Sun.COM 			uint32_t our;
60910506SBarry.Harding@Sun.COM 
61010506SBarry.Harding@Sun.COM 			CSR_WRITE_2(dev, B0_CTST, Y2_HW_WOL_OFF);
61110506SBarry.Harding@Sun.COM 
61210506SBarry.Harding@Sun.COM 			/* Enable all clocks. */
61310506SBarry.Harding@Sun.COM 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
61410506SBarry.Harding@Sun.COM 
61510506SBarry.Harding@Sun.COM 			our = pci_config_get32(dev->d_pcih, PCI_OUR_REG_4);
61610506SBarry.Harding@Sun.COM 			our &= (PCI_FORCE_ASPM_REQUEST|PCI_ASPM_GPHY_LINK_DOWN|
61710506SBarry.Harding@Sun.COM 			    PCI_ASPM_INT_FIFO_EMPTY|PCI_ASPM_CLKRUN_REQUEST);
61810506SBarry.Harding@Sun.COM 			/* Set all bits to 0 except bits 15..12. */
61910506SBarry.Harding@Sun.COM 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_4, our);
62010506SBarry.Harding@Sun.COM 
62110506SBarry.Harding@Sun.COM 			/* Set to default value. */
62210506SBarry.Harding@Sun.COM 			our = pci_config_get32(dev->d_pcih, PCI_OUR_REG_5);
62310506SBarry.Harding@Sun.COM 			our &= P_CTL_TIM_VMAIN_AV_MSK;
62410506SBarry.Harding@Sun.COM 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_5, our);
62510506SBarry.Harding@Sun.COM 
62610506SBarry.Harding@Sun.COM 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, 0);
62710506SBarry.Harding@Sun.COM 
62810506SBarry.Harding@Sun.COM 			/*
62910506SBarry.Harding@Sun.COM 			 * Enable workaround for dev 4.107 on Yukon-Ultra
63010506SBarry.Harding@Sun.COM 			 * and Extreme
63110506SBarry.Harding@Sun.COM 			 */
63210506SBarry.Harding@Sun.COM 			our = CSR_READ_4(dev, B2_GP_IO);
63310506SBarry.Harding@Sun.COM 			our |= GLB_GPIO_STAT_RACE_DIS;
63410506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, B2_GP_IO, our);
63510506SBarry.Harding@Sun.COM 
63610506SBarry.Harding@Sun.COM 			(void) CSR_READ_4(dev, B2_GP_IO);
63710506SBarry.Harding@Sun.COM 			break;
63810506SBarry.Harding@Sun.COM 		}
63910506SBarry.Harding@Sun.COM 		default:
64010506SBarry.Harding@Sun.COM 			break;
64110506SBarry.Harding@Sun.COM 		}
64210506SBarry.Harding@Sun.COM 
64310506SBarry.Harding@Sun.COM 		for (i = 0; i < dev->d_num_port; i++) {
64410506SBarry.Harding@Sun.COM 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_LINK_CTRL),
64510506SBarry.Harding@Sun.COM 			    GMLC_RST_SET);
64610506SBarry.Harding@Sun.COM 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_LINK_CTRL),
64710506SBarry.Harding@Sun.COM 			    GMLC_RST_CLR);
64810506SBarry.Harding@Sun.COM 		}
64910506SBarry.Harding@Sun.COM 	} else {
65010506SBarry.Harding@Sun.COM 		val = pci_config_get32(dev->d_pcih, PCI_OUR_REG_1);
65110506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
65210506SBarry.Harding@Sun.COM 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
65310506SBarry.Harding@Sun.COM 			val &= ~PCI_Y2_PHY1_COMA;
65410506SBarry.Harding@Sun.COM 			if (dev->d_num_port > 1)
65510506SBarry.Harding@Sun.COM 				val &= ~PCI_Y2_PHY2_COMA;
65610506SBarry.Harding@Sun.COM 			val &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
65710506SBarry.Harding@Sun.COM 		} else {
65810506SBarry.Harding@Sun.COM 			val |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
65910506SBarry.Harding@Sun.COM 		}
66010506SBarry.Harding@Sun.COM 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, val);
66110506SBarry.Harding@Sun.COM 
66210506SBarry.Harding@Sun.COM 		val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
66310506SBarry.Harding@Sun.COM 		    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
66410506SBarry.Harding@Sun.COM 		    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS;
66510506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
66610506SBarry.Harding@Sun.COM 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
66710506SBarry.Harding@Sun.COM 			/* Enable bits are inverted. */
66810506SBarry.Harding@Sun.COM 			val = 0;
66910506SBarry.Harding@Sun.COM 		}
67010506SBarry.Harding@Sun.COM 		/*
67110506SBarry.Harding@Sun.COM 		 * Disable PCI & Core Clock, disable clock gating for
67210506SBarry.Harding@Sun.COM 		 * both Links.
67310506SBarry.Harding@Sun.COM 		 */
67410506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_Y2_CLK_GATE, val);
67510506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B0_POWER_CTRL,
67610506SBarry.Harding@Sun.COM 		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF);
67710506SBarry.Harding@Sun.COM 	}
67810506SBarry.Harding@Sun.COM }
67910506SBarry.Harding@Sun.COM 
68010506SBarry.Harding@Sun.COM static void
yge_reset(yge_dev_t * dev)68110506SBarry.Harding@Sun.COM yge_reset(yge_dev_t *dev)
68210506SBarry.Harding@Sun.COM {
68310506SBarry.Harding@Sun.COM 	uint64_t addr;
68410506SBarry.Harding@Sun.COM 	uint16_t status;
68510506SBarry.Harding@Sun.COM 	uint32_t val;
68610506SBarry.Harding@Sun.COM 	int i;
68710506SBarry.Harding@Sun.COM 	ddi_acc_handle_t	pcih = dev->d_pcih;
68810506SBarry.Harding@Sun.COM 
68910506SBarry.Harding@Sun.COM 	/* Turn off ASF */
69010506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_EX) {
69110506SBarry.Harding@Sun.COM 		status = CSR_READ_2(dev, B28_Y2_ASF_STAT_CMD);
69210506SBarry.Harding@Sun.COM 		/* Clear AHB bridge & microcontroller reset */
69310506SBarry.Harding@Sun.COM 		status &= ~Y2_ASF_CPU_MODE;
69410506SBarry.Harding@Sun.COM 		status &= ~Y2_ASF_AHB_RST;
69510506SBarry.Harding@Sun.COM 		/* Clear ASF microcontroller state */
69610506SBarry.Harding@Sun.COM 		status &= ~Y2_ASF_STAT_MSK;
69710506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, B28_Y2_ASF_STAT_CMD, status);
69810506SBarry.Harding@Sun.COM 	} else {
69910506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
70010506SBarry.Harding@Sun.COM 	}
70110506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, Y2_ASF_DISABLE);
70210506SBarry.Harding@Sun.COM 
70310506SBarry.Harding@Sun.COM 	/*
70410506SBarry.Harding@Sun.COM 	 * Since we disabled ASF, S/W reset is required for Power Management.
70510506SBarry.Harding@Sun.COM 	 */
70610506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B0_CTST, CS_RST_SET);
70710506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B0_CTST, CS_RST_CLR);
70810506SBarry.Harding@Sun.COM 
70910506SBarry.Harding@Sun.COM 	/* Allow writes to PCI config space */
71010506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
71110506SBarry.Harding@Sun.COM 
71210506SBarry.Harding@Sun.COM 	/* Clear all error bits in the PCI status register. */
71310506SBarry.Harding@Sun.COM 	status = pci_config_get16(pcih, PCI_CONF_STAT);
71410506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
71510506SBarry.Harding@Sun.COM 
71610506SBarry.Harding@Sun.COM 	status |= (PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB |
71710506SBarry.Harding@Sun.COM 	    PCI_STAT_R_TARG_AB | PCI_STAT_PERROR);
71810506SBarry.Harding@Sun.COM 	pci_config_put16(pcih, PCI_CONF_STAT, status);
71910506SBarry.Harding@Sun.COM 
72010506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B0_CTST, CS_MRST_CLR);
72110506SBarry.Harding@Sun.COM 
72210506SBarry.Harding@Sun.COM 	switch (dev->d_bustype) {
72310506SBarry.Harding@Sun.COM 	case PEX_BUS:
72410506SBarry.Harding@Sun.COM 		/* Clear all PEX errors. */
72510506SBarry.Harding@Sun.COM 		CSR_PCI_WRITE_4(dev, Y2_CFG_AER + AER_UNCOR_ERR, 0xffffffff);
72610506SBarry.Harding@Sun.COM 
72710506SBarry.Harding@Sun.COM 		/* is error bit status stuck? */
72810506SBarry.Harding@Sun.COM 		val = CSR_PCI_READ_4(dev, PEX_UNC_ERR_STAT);
72910506SBarry.Harding@Sun.COM 		if ((val & PEX_RX_OV) != 0) {
73010506SBarry.Harding@Sun.COM 			dev->d_intrmask &= ~Y2_IS_HW_ERR;
73110506SBarry.Harding@Sun.COM 			dev->d_intrhwemask &= ~Y2_IS_PCI_EXP;
73210506SBarry.Harding@Sun.COM 		}
73310506SBarry.Harding@Sun.COM 		break;
73410506SBarry.Harding@Sun.COM 	case PCI_BUS:
73510506SBarry.Harding@Sun.COM 		/* Set Cache Line Size to 2 (8 bytes) if configured to 0. */
73610506SBarry.Harding@Sun.COM 		if (pci_config_get8(pcih, PCI_CONF_CACHE_LINESZ) == 0)
73710506SBarry.Harding@Sun.COM 			pci_config_put16(pcih, PCI_CONF_CACHE_LINESZ, 2);
73810506SBarry.Harding@Sun.COM 		break;
73910506SBarry.Harding@Sun.COM 	case PCIX_BUS:
74010506SBarry.Harding@Sun.COM 		/* Set Cache Line Size to 2 (8 bytes) if configured to 0. */
74110506SBarry.Harding@Sun.COM 		if (pci_config_get8(pcih, PCI_CONF_CACHE_LINESZ) == 0)
74210506SBarry.Harding@Sun.COM 			pci_config_put16(pcih, PCI_CONF_CACHE_LINESZ, 2);
74310506SBarry.Harding@Sun.COM 
74410506SBarry.Harding@Sun.COM 		/* Set Cache Line Size opt. */
74510506SBarry.Harding@Sun.COM 		val = pci_config_get32(pcih, PCI_OUR_REG_1);
74610506SBarry.Harding@Sun.COM 		val |= PCI_CLS_OPT;
74710506SBarry.Harding@Sun.COM 		pci_config_put32(pcih, PCI_OUR_REG_1, val);
74810506SBarry.Harding@Sun.COM 		break;
74910506SBarry.Harding@Sun.COM 	}
75010506SBarry.Harding@Sun.COM 
75110506SBarry.Harding@Sun.COM 	/* Set PHY power state. */
75210506SBarry.Harding@Sun.COM 	yge_phy_power(dev, B_TRUE);
75310506SBarry.Harding@Sun.COM 
75410506SBarry.Harding@Sun.COM 	/* Reset GPHY/GMAC Control */
75510506SBarry.Harding@Sun.COM 	for (i = 0; i < dev->d_num_port; i++) {
75610506SBarry.Harding@Sun.COM 		/* GPHY Control reset. */
75710506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(i, GPHY_CTRL), GPC_RST_SET);
75810506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(i, GPHY_CTRL), GPC_RST_CLR);
75910506SBarry.Harding@Sun.COM 		/* GMAC Control reset. */
76010506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(i, GMAC_CTRL), GMC_RST_SET);
76110506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(i, GMAC_CTRL), GMC_RST_CLR);
76210506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_EX ||
76310506SBarry.Harding@Sun.COM 		    dev->d_hw_id == CHIP_ID_YUKON_SUPR) {
76410506SBarry.Harding@Sun.COM 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_CTRL),
76510506SBarry.Harding@Sun.COM 			    (GMC_BYP_RETR_ON | GMC_BYP_MACSECRX_ON |
76610506SBarry.Harding@Sun.COM 			    GMC_BYP_MACSECTX_ON));
76710506SBarry.Harding@Sun.COM 		}
76810506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, MR_ADDR(i, GMAC_CTRL), GMC_F_LOOPB_OFF);
76910506SBarry.Harding@Sun.COM 
77010506SBarry.Harding@Sun.COM 	}
77110506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
77210506SBarry.Harding@Sun.COM 
77310506SBarry.Harding@Sun.COM 	/* LED On. */
77410506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, Y2_LED_STAT_ON);
77510506SBarry.Harding@Sun.COM 
77610506SBarry.Harding@Sun.COM 	/* Clear TWSI IRQ. */
77710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B2_I2C_IRQ, I2C_CLR_IRQ);
77810506SBarry.Harding@Sun.COM 
77910506SBarry.Harding@Sun.COM 	/* Turn off hardware timer. */
78010506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TI_CTRL, TIM_STOP);
78110506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TI_CTRL, TIM_CLR_IRQ);
78210506SBarry.Harding@Sun.COM 
78310506SBarry.Harding@Sun.COM 	/* Turn off descriptor polling. */
78410506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B28_DPT_CTRL, DPT_STOP);
78510506SBarry.Harding@Sun.COM 
78610506SBarry.Harding@Sun.COM 	/* Turn off time stamps. */
78710506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_STOP);
78810506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
78910506SBarry.Harding@Sun.COM 
79010506SBarry.Harding@Sun.COM 	/* Don't permit config space writing */
79110506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
79210506SBarry.Harding@Sun.COM 
79310506SBarry.Harding@Sun.COM 	/* enable TX Arbiters */
79410506SBarry.Harding@Sun.COM 	for (i = 0; i < dev->d_num_port; i++)
79510506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, MR_ADDR(i, TXA_CTRL), TXA_ENA_ARB);
79610506SBarry.Harding@Sun.COM 
79710506SBarry.Harding@Sun.COM 	/* Configure timeout values. */
79810506SBarry.Harding@Sun.COM 	for (i = 0; i < dev->d_num_port; i++) {
79910506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR);
80010506SBarry.Harding@Sun.COM 
80110506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_R1), RI_TO_53);
80210506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XA1), RI_TO_53);
80310506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XS1), RI_TO_53);
80410506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_R1), RI_TO_53);
80510506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XA1), RI_TO_53);
80610506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XS1), RI_TO_53);
80710506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_R2), RI_TO_53);
80810506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XA2), RI_TO_53);
80910506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XS2), RI_TO_53);
81010506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_R2), RI_TO_53);
81110506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XA2), RI_TO_53);
81210506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XS2), RI_TO_53);
81310506SBarry.Harding@Sun.COM 	}
81410506SBarry.Harding@Sun.COM 
81510506SBarry.Harding@Sun.COM 	/* Disable all interrupts. */
81610506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
81710506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
81810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, 0);
81910506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
82010506SBarry.Harding@Sun.COM 
82110506SBarry.Harding@Sun.COM 	/*
82210506SBarry.Harding@Sun.COM 	 * On dual port PCI-X card, there is an problem where status
82310506SBarry.Harding@Sun.COM 	 * can be received out of order due to split transactions.
82410506SBarry.Harding@Sun.COM 	 */
82510506SBarry.Harding@Sun.COM 	if (dev->d_bustype == PCIX_BUS && dev->d_num_port > 1) {
82610506SBarry.Harding@Sun.COM 		int pcix;
82710506SBarry.Harding@Sun.COM 		uint16_t pcix_cmd;
82810506SBarry.Harding@Sun.COM 
82910506SBarry.Harding@Sun.COM 		if ((pcix = yge_find_capability(dev, PCI_CAP_ID_PCIX)) != 0) {
83010506SBarry.Harding@Sun.COM 			pcix_cmd = pci_config_get16(pcih, pcix + 2);
83110506SBarry.Harding@Sun.COM 			/* Clear Max Outstanding Split Transactions. */
83210506SBarry.Harding@Sun.COM 			pcix_cmd &= ~0x70;
83310506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
83410506SBarry.Harding@Sun.COM 			pci_config_put16(pcih, pcix + 2, pcix_cmd);
83510506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
83610506SBarry.Harding@Sun.COM 		}
83710506SBarry.Harding@Sun.COM 	}
83810506SBarry.Harding@Sun.COM 	if (dev->d_bustype == PEX_BUS) {
83910506SBarry.Harding@Sun.COM 		uint16_t v, width;
84010506SBarry.Harding@Sun.COM 
84110506SBarry.Harding@Sun.COM 		v = pci_config_get16(pcih, PEX_DEV_CTRL);
84210506SBarry.Harding@Sun.COM 		/* Change Max. Read Request Size to 4096 bytes. */
84310506SBarry.Harding@Sun.COM 		v &= ~PEX_DC_MAX_RRS_MSK;
84410506SBarry.Harding@Sun.COM 		v |= PEX_DC_MAX_RD_RQ_SIZE(5);
84510506SBarry.Harding@Sun.COM 		pci_config_put16(pcih, PEX_DEV_CTRL, v);
84610506SBarry.Harding@Sun.COM 		width = pci_config_get16(pcih, PEX_LNK_STAT);
84710506SBarry.Harding@Sun.COM 		width = (width & PEX_LS_LINK_WI_MSK) >> 4;
84810506SBarry.Harding@Sun.COM 		v = pci_config_get16(pcih, PEX_LNK_CAP);
84910506SBarry.Harding@Sun.COM 		v = (v & PEX_LS_LINK_WI_MSK) >> 4;
85010506SBarry.Harding@Sun.COM 		if (v != width)
85110506SBarry.Harding@Sun.COM 			yge_error(dev, NULL,
85210506SBarry.Harding@Sun.COM 			    "Negotiated width of PCIe link(x%d) != "
85310506SBarry.Harding@Sun.COM 			    "max. width of link(x%d)\n", width, v);
85410506SBarry.Harding@Sun.COM 	}
85510506SBarry.Harding@Sun.COM 
85610506SBarry.Harding@Sun.COM 	/* Clear status list. */
85710506SBarry.Harding@Sun.COM 	CLEARRING(&dev->d_status_ring);
85810506SBarry.Harding@Sun.COM 	SYNCRING(&dev->d_status_ring, DDI_DMA_SYNC_FORDEV);
85910506SBarry.Harding@Sun.COM 
86010506SBarry.Harding@Sun.COM 	dev->d_stat_cons = 0;
86110506SBarry.Harding@Sun.COM 
86210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_RST_SET);
86310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_RST_CLR);
86410506SBarry.Harding@Sun.COM 
86510506SBarry.Harding@Sun.COM 	/* Set the status list base address. */
86610506SBarry.Harding@Sun.COM 	addr = dev->d_status_ring.r_paddr;
86710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_LIST_ADDR_LO, YGE_ADDR_LO(addr));
86810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_LIST_ADDR_HI, YGE_ADDR_HI(addr));
86910506SBarry.Harding@Sun.COM 
87010506SBarry.Harding@Sun.COM 	/* Set the status list last index. */
87110506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, STAT_LAST_IDX, YGE_STAT_RING_CNT - 1);
87210506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, STAT_PUT_IDX, 0);
87310506SBarry.Harding@Sun.COM 
87410506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_EC &&
87510506SBarry.Harding@Sun.COM 	    dev->d_hw_rev == CHIP_REV_YU_EC_A1) {
87610506SBarry.Harding@Sun.COM 		/* WA for dev. #4.3 */
87710506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, STAT_TX_IDX_TH, ST_TXTH_IDX_MASK);
87810506SBarry.Harding@Sun.COM 		/* WA for dev #4.18 */
87910506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, STAT_FIFO_WM, 0x21);
88010506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 7);
88110506SBarry.Harding@Sun.COM 	} else {
88210506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, STAT_TX_IDX_TH, 10);
88310506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, STAT_FIFO_WM, 16);
88410506SBarry.Harding@Sun.COM 
88510506SBarry.Harding@Sun.COM 		/* ISR status FIFO watermark */
88610506SBarry.Harding@Sun.COM 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
88710506SBarry.Harding@Sun.COM 		    dev->d_hw_rev == CHIP_REV_YU_XL_A0)
88810506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 4);
88910506SBarry.Harding@Sun.COM 		else
89010506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 16);
89110506SBarry.Harding@Sun.COM 
89210506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, STAT_ISR_TIMER_INI, 0x0190);
89310506SBarry.Harding@Sun.COM 	}
89410506SBarry.Harding@Sun.COM 
89510506SBarry.Harding@Sun.COM 	/*
89610506SBarry.Harding@Sun.COM 	 * Use default value for STAT_ISR_TIMER_INI, STAT_LEV_TIMER_INI.
89710506SBarry.Harding@Sun.COM 	 */
89810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_TX_TIMER_INI, YGE_USECS(dev, 1000));
89910506SBarry.Harding@Sun.COM 
90010506SBarry.Harding@Sun.COM 	/* Enable status unit. */
90110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_OP_ON);
90210506SBarry.Harding@Sun.COM 
90310506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, STAT_TX_TIMER_CTRL, TIM_START);
90410506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, STAT_LEV_TIMER_CTRL, TIM_START);
90510506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, STAT_ISR_TIMER_CTRL, TIM_START);
90610506SBarry.Harding@Sun.COM }
90710506SBarry.Harding@Sun.COM 
90810506SBarry.Harding@Sun.COM static int
yge_init_port(yge_port_t * port)90910506SBarry.Harding@Sun.COM yge_init_port(yge_port_t *port)
91010506SBarry.Harding@Sun.COM {
91110506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
91210506SBarry.Harding@Sun.COM 	int i;
91310506SBarry.Harding@Sun.COM 	mac_register_t *macp;
91410506SBarry.Harding@Sun.COM 
91510506SBarry.Harding@Sun.COM 	port->p_flags = dev->d_pflags;
91610506SBarry.Harding@Sun.COM 	port->p_ppa = ddi_get_instance(dev->d_dip) + (port->p_port * 100);
91710506SBarry.Harding@Sun.COM 
91810506SBarry.Harding@Sun.COM 	port->p_tx_buf = kmem_zalloc(sizeof (yge_buf_t) * YGE_TX_RING_CNT,
91910506SBarry.Harding@Sun.COM 	    KM_SLEEP);
92010506SBarry.Harding@Sun.COM 	port->p_rx_buf = kmem_zalloc(sizeof (yge_buf_t) * YGE_RX_RING_CNT,
92110506SBarry.Harding@Sun.COM 	    KM_SLEEP);
92210506SBarry.Harding@Sun.COM 
92310506SBarry.Harding@Sun.COM 	/* Setup Tx/Rx queue register offsets. */
92410506SBarry.Harding@Sun.COM 	if (port->p_port == YGE_PORT_A) {
92510506SBarry.Harding@Sun.COM 		port->p_txq = Q_XA1;
92610506SBarry.Harding@Sun.COM 		port->p_txsq = Q_XS1;
92710506SBarry.Harding@Sun.COM 		port->p_rxq = Q_R1;
92810506SBarry.Harding@Sun.COM 	} else {
92910506SBarry.Harding@Sun.COM 		port->p_txq = Q_XA2;
93010506SBarry.Harding@Sun.COM 		port->p_txsq = Q_XS2;
93110506SBarry.Harding@Sun.COM 		port->p_rxq = Q_R2;
93210506SBarry.Harding@Sun.COM 	}
93310506SBarry.Harding@Sun.COM 
93410506SBarry.Harding@Sun.COM 	/* Disable jumbo frame for Yukon FE. */
93510506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_FE)
93610506SBarry.Harding@Sun.COM 		port->p_flags |= PORT_FLAG_NOJUMBO;
93710506SBarry.Harding@Sun.COM 
93810506SBarry.Harding@Sun.COM 	/*
93910506SBarry.Harding@Sun.COM 	 * Start out assuming a regular MTU.  Users can change this
94010506SBarry.Harding@Sun.COM 	 * with dladm.  The dladm daemon is supposed to issue commands
94110506SBarry.Harding@Sun.COM 	 * to change the default MTU using m_setprop during early boot
94210506SBarry.Harding@Sun.COM 	 * (before the interface is plumbed) if the user has so
94310506SBarry.Harding@Sun.COM 	 * requested.
94410506SBarry.Harding@Sun.COM 	 */
94510506SBarry.Harding@Sun.COM 	port->p_mtu = ETHERMTU;
94610506SBarry.Harding@Sun.COM 
94710506SBarry.Harding@Sun.COM 	port->p_mii = mii_alloc(port, dev->d_dip, &yge_mii_ops);
94810506SBarry.Harding@Sun.COM 	if (port->p_mii == NULL) {
94910506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "MII handle allocation failed");
95010506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
95110506SBarry.Harding@Sun.COM 	}
95210506SBarry.Harding@Sun.COM 	/* We assume all parts support asymmetric pause */
95310506SBarry.Harding@Sun.COM 	mii_set_pauseable(port->p_mii, B_TRUE, B_TRUE);
95410506SBarry.Harding@Sun.COM 
95510506SBarry.Harding@Sun.COM 	/*
95610506SBarry.Harding@Sun.COM 	 * Get station address for this interface. Note that
95710506SBarry.Harding@Sun.COM 	 * dual port cards actually come with three station
95810506SBarry.Harding@Sun.COM 	 * addresses: one for each port, plus an extra. The
95910506SBarry.Harding@Sun.COM 	 * extra one is used by the SysKonnect driver software
96010506SBarry.Harding@Sun.COM 	 * as a 'virtual' station address for when both ports
96110506SBarry.Harding@Sun.COM 	 * are operating in failover mode. Currently we don't
96210506SBarry.Harding@Sun.COM 	 * use this extra address.
96310506SBarry.Harding@Sun.COM 	 */
96410506SBarry.Harding@Sun.COM 	for (i = 0; i < ETHERADDRL; i++) {
96510506SBarry.Harding@Sun.COM 		port->p_curraddr[i] =
96610506SBarry.Harding@Sun.COM 		    CSR_READ_1(dev, B2_MAC_1 + (port->p_port * 8) + i);
96710506SBarry.Harding@Sun.COM 	}
96810506SBarry.Harding@Sun.COM 
96910506SBarry.Harding@Sun.COM 	/* Register with Nemo. */
97010506SBarry.Harding@Sun.COM 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
97110506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "MAC handle allocation failed");
97210506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
97310506SBarry.Harding@Sun.COM 	}
97410506SBarry.Harding@Sun.COM 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
97510506SBarry.Harding@Sun.COM 	macp->m_driver = port;
97610506SBarry.Harding@Sun.COM 	macp->m_dip = dev->d_dip;
97710506SBarry.Harding@Sun.COM 	macp->m_src_addr = port->p_curraddr;
97810506SBarry.Harding@Sun.COM 	macp->m_callbacks = &yge_m_callbacks;
97910506SBarry.Harding@Sun.COM 	macp->m_min_sdu = 0;
98010506SBarry.Harding@Sun.COM 	macp->m_max_sdu = port->p_mtu;
98110506SBarry.Harding@Sun.COM 	macp->m_instance = port->p_ppa;
98210506SBarry.Harding@Sun.COM 	macp->m_margin = VLAN_TAGSZ;
98310506SBarry.Harding@Sun.COM 
98410506SBarry.Harding@Sun.COM 	port->p_mreg = macp;
98510506SBarry.Harding@Sun.COM 
98610506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
98710506SBarry.Harding@Sun.COM }
98810506SBarry.Harding@Sun.COM 
98910506SBarry.Harding@Sun.COM static int
yge_add_intr(yge_dev_t * dev,int intr_type)99010506SBarry.Harding@Sun.COM yge_add_intr(yge_dev_t *dev, int intr_type)
99110506SBarry.Harding@Sun.COM {
99210506SBarry.Harding@Sun.COM 	dev_info_t		*dip;
99310506SBarry.Harding@Sun.COM 	int			count;
99410506SBarry.Harding@Sun.COM 	int			actual;
99510506SBarry.Harding@Sun.COM 	int			rv;
99610506SBarry.Harding@Sun.COM 	int 			i, j;
99710506SBarry.Harding@Sun.COM 
99810506SBarry.Harding@Sun.COM 	dip = dev->d_dip;
99910506SBarry.Harding@Sun.COM 
100010506SBarry.Harding@Sun.COM 	rv = ddi_intr_get_nintrs(dip, intr_type, &count);
100110506SBarry.Harding@Sun.COM 	if ((rv != DDI_SUCCESS) || (count == 0)) {
100210506SBarry.Harding@Sun.COM 		yge_error(dev, NULL,
100310506SBarry.Harding@Sun.COM 		    "ddi_intr_get_nintrs failed, rv %d, count %d", rv, count);
100410506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
100510506SBarry.Harding@Sun.COM 	}
100610506SBarry.Harding@Sun.COM 
100710506SBarry.Harding@Sun.COM 	/*
100810506SBarry.Harding@Sun.COM 	 * Allocate the interrupt.  Note that we only bother with a single
100910506SBarry.Harding@Sun.COM 	 * interrupt.  One could argue that for MSI devices with dual ports,
101010506SBarry.Harding@Sun.COM 	 * it would be nice to have a separate interrupt per port.  But right
101110506SBarry.Harding@Sun.COM 	 * now I don't know how to configure that, so we'll just settle for
101210506SBarry.Harding@Sun.COM 	 * a single interrupt.
101310506SBarry.Harding@Sun.COM 	 */
101410506SBarry.Harding@Sun.COM 	dev->d_intrcnt = 1;
101510506SBarry.Harding@Sun.COM 
101610506SBarry.Harding@Sun.COM 	dev->d_intrsize = count * sizeof (ddi_intr_handle_t);
101710506SBarry.Harding@Sun.COM 	dev->d_intrh = kmem_zalloc(dev->d_intrsize, KM_SLEEP);
101810506SBarry.Harding@Sun.COM 	if (dev->d_intrh == NULL) {
101910506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "Unable to allocate interrupt handle");
102010506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
102110506SBarry.Harding@Sun.COM 	}
102210506SBarry.Harding@Sun.COM 
102310506SBarry.Harding@Sun.COM 	rv = ddi_intr_alloc(dip, dev->d_intrh, intr_type, 0, dev->d_intrcnt,
102410506SBarry.Harding@Sun.COM 	    &actual, DDI_INTR_ALLOC_STRICT);
102510506SBarry.Harding@Sun.COM 	if ((rv != DDI_SUCCESS) || (actual == 0)) {
102610506SBarry.Harding@Sun.COM 		yge_error(dev, NULL,
102710506SBarry.Harding@Sun.COM 		    "Unable to allocate interrupt, %d, count %d",
102810506SBarry.Harding@Sun.COM 		    rv, actual);
102910506SBarry.Harding@Sun.COM 		kmem_free(dev->d_intrh, dev->d_intrsize);
103010506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
103110506SBarry.Harding@Sun.COM 	}
103210506SBarry.Harding@Sun.COM 
103310506SBarry.Harding@Sun.COM 	if ((rv = ddi_intr_get_pri(dev->d_intrh[0], &dev->d_intrpri)) !=
103410506SBarry.Harding@Sun.COM 	    DDI_SUCCESS) {
103510506SBarry.Harding@Sun.COM 		for (i = 0; i < dev->d_intrcnt; i++)
103610506SBarry.Harding@Sun.COM 			(void) ddi_intr_free(dev->d_intrh[i]);
103710506SBarry.Harding@Sun.COM 		yge_error(dev, NULL,
103810506SBarry.Harding@Sun.COM 		    "Unable to get interrupt priority, %d", rv);
103910506SBarry.Harding@Sun.COM 		kmem_free(dev->d_intrh, dev->d_intrsize);
104010506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
104110506SBarry.Harding@Sun.COM 	}
104210506SBarry.Harding@Sun.COM 
104310506SBarry.Harding@Sun.COM 	if ((rv = ddi_intr_get_cap(dev->d_intrh[0], &dev->d_intrcap)) !=
104410506SBarry.Harding@Sun.COM 	    DDI_SUCCESS) {
104510506SBarry.Harding@Sun.COM 		yge_error(dev, NULL,
104610506SBarry.Harding@Sun.COM 		    "Unable to get interrupt capabilities, %d", rv);
104710506SBarry.Harding@Sun.COM 		for (i = 0; i < dev->d_intrcnt; i++)
104810506SBarry.Harding@Sun.COM 			(void) ddi_intr_free(dev->d_intrh[i]);
104910506SBarry.Harding@Sun.COM 		kmem_free(dev->d_intrh, dev->d_intrsize);
105010506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
105110506SBarry.Harding@Sun.COM 	}
105210506SBarry.Harding@Sun.COM 
105310506SBarry.Harding@Sun.COM 	/* register interrupt handler to kernel */
105410506SBarry.Harding@Sun.COM 	for (i = 0; i < dev->d_intrcnt; i++) {
105510506SBarry.Harding@Sun.COM 		if ((rv = ddi_intr_add_handler(dev->d_intrh[i], yge_intr,
105610506SBarry.Harding@Sun.COM 		    dev, NULL)) != DDI_SUCCESS) {
105710506SBarry.Harding@Sun.COM 			yge_error(dev, NULL,
105810506SBarry.Harding@Sun.COM 			    "Unable to add interrupt handler, %d", rv);
105910506SBarry.Harding@Sun.COM 			for (j = 0; j < i; j++)
106010506SBarry.Harding@Sun.COM 				(void) ddi_intr_remove_handler(dev->d_intrh[j]);
106110506SBarry.Harding@Sun.COM 			for (i = 0; i < dev->d_intrcnt; i++)
106210506SBarry.Harding@Sun.COM 				(void) ddi_intr_free(dev->d_intrh[i]);
106310506SBarry.Harding@Sun.COM 			kmem_free(dev->d_intrh, dev->d_intrsize);
106410506SBarry.Harding@Sun.COM 			return (DDI_FAILURE);
106510506SBarry.Harding@Sun.COM 		}
106610506SBarry.Harding@Sun.COM 	}
106710506SBarry.Harding@Sun.COM 
106810506SBarry.Harding@Sun.COM 	mutex_init(&dev->d_rxlock, NULL, MUTEX_DRIVER,
106910506SBarry.Harding@Sun.COM 	    DDI_INTR_PRI(dev->d_intrpri));
107010506SBarry.Harding@Sun.COM 	mutex_init(&dev->d_txlock, NULL, MUTEX_DRIVER,
107110506SBarry.Harding@Sun.COM 	    DDI_INTR_PRI(dev->d_intrpri));
107210506SBarry.Harding@Sun.COM 	mutex_init(&dev->d_phylock, NULL, MUTEX_DRIVER,
107310506SBarry.Harding@Sun.COM 	    DDI_INTR_PRI(dev->d_intrpri));
107410506SBarry.Harding@Sun.COM 	mutex_init(&dev->d_task_mtx, NULL, MUTEX_DRIVER,
107510506SBarry.Harding@Sun.COM 	    DDI_INTR_PRI(dev->d_intrpri));
107610506SBarry.Harding@Sun.COM 
107710506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
107810506SBarry.Harding@Sun.COM }
107910506SBarry.Harding@Sun.COM 
108010506SBarry.Harding@Sun.COM static int
yge_attach_intr(yge_dev_t * dev)108110506SBarry.Harding@Sun.COM yge_attach_intr(yge_dev_t *dev)
108210506SBarry.Harding@Sun.COM {
108310506SBarry.Harding@Sun.COM 	dev_info_t *dip = dev->d_dip;
108410506SBarry.Harding@Sun.COM 	int intr_types;
108510506SBarry.Harding@Sun.COM 	int rv;
108610506SBarry.Harding@Sun.COM 
108710506SBarry.Harding@Sun.COM 	/* Allocate IRQ resources. */
108810506SBarry.Harding@Sun.COM 	rv = ddi_intr_get_supported_types(dip, &intr_types);
108910506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
109010506SBarry.Harding@Sun.COM 		yge_error(dev, NULL,
109110506SBarry.Harding@Sun.COM 		    "Unable to determine supported interrupt types, %d", rv);
109210506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
109310506SBarry.Harding@Sun.COM 	}
109410506SBarry.Harding@Sun.COM 
109510506SBarry.Harding@Sun.COM 	/*
109610506SBarry.Harding@Sun.COM 	 * We default to not supporting MSI.  We've found some device
109710506SBarry.Harding@Sun.COM 	 * and motherboard combinations don't always work well with
109810506SBarry.Harding@Sun.COM 	 * MSI interrupts.  Users may override this if they choose.
109910506SBarry.Harding@Sun.COM 	 */
110010506SBarry.Harding@Sun.COM 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "msi_enable", 0) == 0) {
110110506SBarry.Harding@Sun.COM 		/* If msi disable property present, disable both msix/msi. */
110210506SBarry.Harding@Sun.COM 		if (intr_types & DDI_INTR_TYPE_FIXED) {
110310506SBarry.Harding@Sun.COM 			intr_types &= ~(DDI_INTR_TYPE_MSI | DDI_INTR_TYPE_MSIX);
110410506SBarry.Harding@Sun.COM 		}
110510506SBarry.Harding@Sun.COM 	}
110610506SBarry.Harding@Sun.COM 
110710506SBarry.Harding@Sun.COM 	if (intr_types & DDI_INTR_TYPE_MSIX) {
110810506SBarry.Harding@Sun.COM 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_MSIX)) ==
110910506SBarry.Harding@Sun.COM 		    DDI_SUCCESS)
111010506SBarry.Harding@Sun.COM 			return (DDI_SUCCESS);
111110506SBarry.Harding@Sun.COM 	}
111210506SBarry.Harding@Sun.COM 
111310506SBarry.Harding@Sun.COM 	if (intr_types & DDI_INTR_TYPE_MSI) {
111410506SBarry.Harding@Sun.COM 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_MSI)) ==
111510506SBarry.Harding@Sun.COM 		    DDI_SUCCESS)
111610506SBarry.Harding@Sun.COM 			return (DDI_SUCCESS);
111710506SBarry.Harding@Sun.COM 	}
111810506SBarry.Harding@Sun.COM 
111910506SBarry.Harding@Sun.COM 	if (intr_types & DDI_INTR_TYPE_FIXED) {
112010506SBarry.Harding@Sun.COM 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_FIXED)) ==
112110506SBarry.Harding@Sun.COM 		    DDI_SUCCESS)
112210506SBarry.Harding@Sun.COM 			return (DDI_SUCCESS);
112310506SBarry.Harding@Sun.COM 	}
112410506SBarry.Harding@Sun.COM 
112510506SBarry.Harding@Sun.COM 	yge_error(dev, NULL, "Unable to configure any interrupts");
112610506SBarry.Harding@Sun.COM 	return (DDI_FAILURE);
112710506SBarry.Harding@Sun.COM }
112810506SBarry.Harding@Sun.COM 
112910506SBarry.Harding@Sun.COM static void
yge_intr_enable(yge_dev_t * dev)113010506SBarry.Harding@Sun.COM yge_intr_enable(yge_dev_t *dev)
113110506SBarry.Harding@Sun.COM {
113210506SBarry.Harding@Sun.COM 	int i;
113310506SBarry.Harding@Sun.COM 	if (dev->d_intrcap & DDI_INTR_FLAG_BLOCK) {
113410506SBarry.Harding@Sun.COM 		/* Call ddi_intr_block_enable() for MSI interrupts */
113510506SBarry.Harding@Sun.COM 		(void) ddi_intr_block_enable(dev->d_intrh, dev->d_intrcnt);
113610506SBarry.Harding@Sun.COM 	} else {
113710506SBarry.Harding@Sun.COM 		/* Call ddi_intr_enable for FIXED interrupts */
113810506SBarry.Harding@Sun.COM 		for (i = 0; i < dev->d_intrcnt; i++)
113910506SBarry.Harding@Sun.COM 			(void) ddi_intr_enable(dev->d_intrh[i]);
114010506SBarry.Harding@Sun.COM 	}
114110506SBarry.Harding@Sun.COM }
114210506SBarry.Harding@Sun.COM 
114310506SBarry.Harding@Sun.COM void
yge_intr_disable(yge_dev_t * dev)114410506SBarry.Harding@Sun.COM yge_intr_disable(yge_dev_t *dev)
114510506SBarry.Harding@Sun.COM {
114610506SBarry.Harding@Sun.COM 	int i;
114710506SBarry.Harding@Sun.COM 
114810506SBarry.Harding@Sun.COM 	if (dev->d_intrcap & DDI_INTR_FLAG_BLOCK) {
114910506SBarry.Harding@Sun.COM 		(void) ddi_intr_block_disable(dev->d_intrh, dev->d_intrcnt);
115010506SBarry.Harding@Sun.COM 	} else {
115110506SBarry.Harding@Sun.COM 		for (i = 0; i < dev->d_intrcnt; i++)
115210506SBarry.Harding@Sun.COM 			(void) ddi_intr_disable(dev->d_intrh[i]);
115310506SBarry.Harding@Sun.COM 	}
115410506SBarry.Harding@Sun.COM }
115510506SBarry.Harding@Sun.COM 
115610506SBarry.Harding@Sun.COM static uint8_t
yge_find_capability(yge_dev_t * dev,uint8_t cap)115710506SBarry.Harding@Sun.COM yge_find_capability(yge_dev_t *dev, uint8_t cap)
115810506SBarry.Harding@Sun.COM {
115910506SBarry.Harding@Sun.COM 	uint8_t ptr;
116010506SBarry.Harding@Sun.COM 	uint16_t capit;
116110506SBarry.Harding@Sun.COM 	ddi_acc_handle_t pcih = dev->d_pcih;
116210506SBarry.Harding@Sun.COM 
116310506SBarry.Harding@Sun.COM 	if ((pci_config_get16(pcih, PCI_CONF_STAT) & PCI_STAT_CAP) == 0) {
116410506SBarry.Harding@Sun.COM 		return (0);
116510506SBarry.Harding@Sun.COM 	}
116610506SBarry.Harding@Sun.COM 	/* This assumes PCI, and not CardBus. */
116710506SBarry.Harding@Sun.COM 	ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
116810506SBarry.Harding@Sun.COM 	while (ptr != 0) {
116910506SBarry.Harding@Sun.COM 		capit = pci_config_get8(pcih, ptr + PCI_CAP_ID);
117010506SBarry.Harding@Sun.COM 		if (capit == cap) {
117110506SBarry.Harding@Sun.COM 			return (ptr);
117210506SBarry.Harding@Sun.COM 		}
117310506SBarry.Harding@Sun.COM 		ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR);
117410506SBarry.Harding@Sun.COM 	}
117510506SBarry.Harding@Sun.COM 	return (0);
117610506SBarry.Harding@Sun.COM }
117710506SBarry.Harding@Sun.COM 
117810506SBarry.Harding@Sun.COM static int
yge_attach(yge_dev_t * dev)117910506SBarry.Harding@Sun.COM yge_attach(yge_dev_t *dev)
118010506SBarry.Harding@Sun.COM {
118110506SBarry.Harding@Sun.COM 	dev_info_t	*dip = dev->d_dip;
118210506SBarry.Harding@Sun.COM 	int		rv;
118310506SBarry.Harding@Sun.COM 	int		nattached;
118410506SBarry.Harding@Sun.COM 	uint8_t		pm_cap;
118510506SBarry.Harding@Sun.COM 
118610506SBarry.Harding@Sun.COM 	if (pci_config_setup(dip, &dev->d_pcih) != DDI_SUCCESS) {
118710506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "Unable to map PCI configuration space");
118810506SBarry.Harding@Sun.COM 		goto fail;
118910506SBarry.Harding@Sun.COM 	}
119010506SBarry.Harding@Sun.COM 
119110506SBarry.Harding@Sun.COM 	/*
119210506SBarry.Harding@Sun.COM 	 * Map control/status registers.
119310506SBarry.Harding@Sun.COM 	 */
119410506SBarry.Harding@Sun.COM 
119510506SBarry.Harding@Sun.COM 	/* ensure the pmcsr status is D0 state */
119610506SBarry.Harding@Sun.COM 	pm_cap = yge_find_capability(dev, PCI_CAP_ID_PM);
119710506SBarry.Harding@Sun.COM 	if (pm_cap != 0) {
119810506SBarry.Harding@Sun.COM 		uint16_t pmcsr;
119910506SBarry.Harding@Sun.COM 		pmcsr = pci_config_get16(dev->d_pcih, pm_cap + PCI_PMCSR);
120010506SBarry.Harding@Sun.COM 		pmcsr &= ~PCI_PMCSR_STATE_MASK;
120110506SBarry.Harding@Sun.COM 		pci_config_put16(dev->d_pcih, pm_cap + PCI_PMCSR,
120210506SBarry.Harding@Sun.COM 		    pmcsr | PCI_PMCSR_D0);
120310506SBarry.Harding@Sun.COM 	}
120410506SBarry.Harding@Sun.COM 
120510506SBarry.Harding@Sun.COM 	/* Enable PCI access and bus master. */
120610506SBarry.Harding@Sun.COM 	pci_config_put16(dev->d_pcih, PCI_CONF_COMM,
120710506SBarry.Harding@Sun.COM 	    pci_config_get16(dev->d_pcih, PCI_CONF_COMM) |
120810506SBarry.Harding@Sun.COM 	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
120910506SBarry.Harding@Sun.COM 
121010506SBarry.Harding@Sun.COM 
121110506SBarry.Harding@Sun.COM 	/* Allocate I/O resource */
121210506SBarry.Harding@Sun.COM 	rv = ddi_regs_map_setup(dip, 1, &dev->d_regs, 0, 0, &yge_regs_attr,
121310506SBarry.Harding@Sun.COM 	    &dev->d_regsh);
121410506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
121510506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "Unable to map device registers");
121610506SBarry.Harding@Sun.COM 		goto fail;
121710506SBarry.Harding@Sun.COM 	}
121810506SBarry.Harding@Sun.COM 
121910506SBarry.Harding@Sun.COM 
122010506SBarry.Harding@Sun.COM 	/* Enable all clocks. */
122110506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
122210506SBarry.Harding@Sun.COM 	pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
122310506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
122410506SBarry.Harding@Sun.COM 
122510506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_CLR);
122610506SBarry.Harding@Sun.COM 	dev->d_hw_id = CSR_READ_1(dev, B2_CHIP_ID);
122710506SBarry.Harding@Sun.COM 	dev->d_hw_rev = (CSR_READ_1(dev, B2_MAC_CFG) >> 4) & 0x0f;
122810506SBarry.Harding@Sun.COM 
122910506SBarry.Harding@Sun.COM 
123010506SBarry.Harding@Sun.COM 	/*
123110506SBarry.Harding@Sun.COM 	 * Bail out if chip is not recognized.  Note that we only enforce
123210506SBarry.Harding@Sun.COM 	 * this in production builds.  The Ultra-2 (88e8057) has a problem
123310506SBarry.Harding@Sun.COM 	 * right now where TX works fine, but RX seems not to.  So we've
123410506SBarry.Harding@Sun.COM 	 * disabled that for now.
123510506SBarry.Harding@Sun.COM 	 */
123610506SBarry.Harding@Sun.COM 	if (dev->d_hw_id < CHIP_ID_YUKON_XL ||
123710506SBarry.Harding@Sun.COM 	    dev->d_hw_id >= CHIP_ID_YUKON_UL_2) {
123810506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "Unknown device: id=0x%02x, rev=0x%02x",
123910506SBarry.Harding@Sun.COM 		    dev->d_hw_id, dev->d_hw_rev);
124010506SBarry.Harding@Sun.COM #ifndef	DEBUG
124110506SBarry.Harding@Sun.COM 		goto fail;
124210506SBarry.Harding@Sun.COM #endif
124310506SBarry.Harding@Sun.COM 	}
124410506SBarry.Harding@Sun.COM 
124510506SBarry.Harding@Sun.COM 	/* Soft reset. */
124610506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
124710506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_CLR);
124810506SBarry.Harding@Sun.COM 	dev->d_pmd = CSR_READ_1(dev, B2_PMD_TYP);
124910506SBarry.Harding@Sun.COM 	if (dev->d_pmd == 'L' || dev->d_pmd == 'S' || dev->d_pmd == 'P')
125010506SBarry.Harding@Sun.COM 		dev->d_coppertype = 0;
125110506SBarry.Harding@Sun.COM 	else
125210506SBarry.Harding@Sun.COM 		dev->d_coppertype = 1;
125310506SBarry.Harding@Sun.COM 	/* Check number of MACs. */
125410506SBarry.Harding@Sun.COM 	dev->d_num_port = 1;
125510506SBarry.Harding@Sun.COM 	if ((CSR_READ_1(dev, B2_Y2_HW_RES) & CFG_DUAL_MAC_MSK) ==
125610506SBarry.Harding@Sun.COM 	    CFG_DUAL_MAC_MSK) {
125710506SBarry.Harding@Sun.COM 		if (!(CSR_READ_1(dev, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC))
125810506SBarry.Harding@Sun.COM 			dev->d_num_port++;
125910506SBarry.Harding@Sun.COM 	}
126010506SBarry.Harding@Sun.COM 
126110506SBarry.Harding@Sun.COM 	/* Check bus type. */
126210506SBarry.Harding@Sun.COM 	if (yge_find_capability(dev, PCI_CAP_ID_PCI_E) != 0) {
126310506SBarry.Harding@Sun.COM 		dev->d_bustype = PEX_BUS;
126410506SBarry.Harding@Sun.COM 	} else if (yge_find_capability(dev, PCI_CAP_ID_PCIX) != 0) {
126510506SBarry.Harding@Sun.COM 		dev->d_bustype = PCIX_BUS;
126610506SBarry.Harding@Sun.COM 	} else {
126710506SBarry.Harding@Sun.COM 		dev->d_bustype = PCI_BUS;
126810506SBarry.Harding@Sun.COM 	}
126910506SBarry.Harding@Sun.COM 
127010506SBarry.Harding@Sun.COM 	switch (dev->d_hw_id) {
127110506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EC:
127210506SBarry.Harding@Sun.COM 		dev->d_clock = 125;	/* 125 Mhz */
127310506SBarry.Harding@Sun.COM 		break;
127410506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_UL_2:
127510506SBarry.Harding@Sun.COM 		dev->d_clock = 125;	/* 125 Mhz */
127610506SBarry.Harding@Sun.COM 		break;
127710506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_SUPR:
127810506SBarry.Harding@Sun.COM 		dev->d_clock = 125;	/* 125 Mhz */
127910506SBarry.Harding@Sun.COM 		break;
128010506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EC_U:
128110506SBarry.Harding@Sun.COM 		dev->d_clock = 125;	/* 125 Mhz */
128210506SBarry.Harding@Sun.COM 		break;
128310506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EX:
128410506SBarry.Harding@Sun.COM 		dev->d_clock = 125;	/* 125 Mhz */
128510506SBarry.Harding@Sun.COM 		break;
128610506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_FE:
128710506SBarry.Harding@Sun.COM 		dev->d_clock = 100;	/* 100 Mhz */
128810506SBarry.Harding@Sun.COM 		break;
128910506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_FE_P:
129010506SBarry.Harding@Sun.COM 		dev->d_clock = 50;	/* 50 Mhz */
129110506SBarry.Harding@Sun.COM 		break;
129210506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_XL:
129310506SBarry.Harding@Sun.COM 		dev->d_clock = 156;	/* 156 Mhz */
129410506SBarry.Harding@Sun.COM 		break;
129510506SBarry.Harding@Sun.COM 	default:
129610506SBarry.Harding@Sun.COM 		dev->d_clock = 156;	/* 156 Mhz */
129710506SBarry.Harding@Sun.COM 		break;
129810506SBarry.Harding@Sun.COM 	}
129910506SBarry.Harding@Sun.COM 
130010506SBarry.Harding@Sun.COM 	dev->d_process_limit = YGE_RX_RING_CNT/2;
130110506SBarry.Harding@Sun.COM 
130210506SBarry.Harding@Sun.COM 	rv = yge_alloc_ring(NULL, dev, &dev->d_status_ring, YGE_STAT_RING_CNT);
130310506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS)
130410506SBarry.Harding@Sun.COM 		goto fail;
130510506SBarry.Harding@Sun.COM 
130610506SBarry.Harding@Sun.COM 	/* Setup event taskq. */
130710506SBarry.Harding@Sun.COM 	dev->d_task_q = ddi_taskq_create(dip, "tq", 1, TASKQ_DEFAULTPRI, 0);
130810506SBarry.Harding@Sun.COM 	if (dev->d_task_q == NULL) {
130910506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "failed to create taskq");
131010506SBarry.Harding@Sun.COM 		goto fail;
131110506SBarry.Harding@Sun.COM 	}
131210506SBarry.Harding@Sun.COM 
131310506SBarry.Harding@Sun.COM 	/* Init the condition variable */
131410506SBarry.Harding@Sun.COM 	cv_init(&dev->d_task_cv, NULL, CV_DRIVER, NULL);
131510506SBarry.Harding@Sun.COM 
131610506SBarry.Harding@Sun.COM 	/* Allocate IRQ resources. */
131710506SBarry.Harding@Sun.COM 	if ((rv = yge_attach_intr(dev)) != DDI_SUCCESS) {
131810506SBarry.Harding@Sun.COM 		goto fail;
131910506SBarry.Harding@Sun.COM 	}
132010506SBarry.Harding@Sun.COM 
132110506SBarry.Harding@Sun.COM 	/* Set base interrupt mask. */
132210506SBarry.Harding@Sun.COM 	dev->d_intrmask = Y2_IS_HW_ERR | Y2_IS_STAT_BMU;
132310506SBarry.Harding@Sun.COM 	dev->d_intrhwemask = Y2_IS_TIST_OV | Y2_IS_MST_ERR |
132410506SBarry.Harding@Sun.COM 	    Y2_IS_IRQ_STAT | Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP;
132510506SBarry.Harding@Sun.COM 
132610506SBarry.Harding@Sun.COM 	/* Reset the adapter. */
132710506SBarry.Harding@Sun.COM 	yge_reset(dev);
132810506SBarry.Harding@Sun.COM 
132910506SBarry.Harding@Sun.COM 	yge_setup_rambuffer(dev);
133010506SBarry.Harding@Sun.COM 
133110506SBarry.Harding@Sun.COM 	nattached = 0;
133210506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
133310506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
133410506SBarry.Harding@Sun.COM 		if (yge_init_port(port) != DDI_SUCCESS) {
133510506SBarry.Harding@Sun.COM 			goto fail;
133610506SBarry.Harding@Sun.COM 		}
133710506SBarry.Harding@Sun.COM 	}
133810506SBarry.Harding@Sun.COM 
133910506SBarry.Harding@Sun.COM 	yge_intr_enable(dev);
134010506SBarry.Harding@Sun.COM 
134110506SBarry.Harding@Sun.COM 	/* set up the periodic to run once per second */
134210506SBarry.Harding@Sun.COM 	dev->d_periodic = ddi_periodic_add(yge_tick, dev, 1000000000, 0);
134310506SBarry.Harding@Sun.COM 
134410506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
134510506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
134610506SBarry.Harding@Sun.COM 		if (yge_register_port(port) == DDI_SUCCESS) {
134710506SBarry.Harding@Sun.COM 			nattached++;
134810506SBarry.Harding@Sun.COM 		}
134910506SBarry.Harding@Sun.COM 	}
135010506SBarry.Harding@Sun.COM 
135110506SBarry.Harding@Sun.COM 	if (nattached == 0) {
135210506SBarry.Harding@Sun.COM 		goto fail;
135310506SBarry.Harding@Sun.COM 	}
135410506SBarry.Harding@Sun.COM 
135510506SBarry.Harding@Sun.COM 	/* Dispatch the taskq */
135610506SBarry.Harding@Sun.COM 	if (ddi_taskq_dispatch(dev->d_task_q, yge_task, dev, DDI_SLEEP) !=
135710506SBarry.Harding@Sun.COM 	    DDI_SUCCESS) {
135810506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "failed to start taskq");
135910506SBarry.Harding@Sun.COM 		goto fail;
136010506SBarry.Harding@Sun.COM 	}
136110506SBarry.Harding@Sun.COM 
136210506SBarry.Harding@Sun.COM 	ddi_report_dev(dip);
136310506SBarry.Harding@Sun.COM 
136410506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
136510506SBarry.Harding@Sun.COM 
136610506SBarry.Harding@Sun.COM fail:
136710506SBarry.Harding@Sun.COM 	yge_detach(dev);
136810506SBarry.Harding@Sun.COM 	return (DDI_FAILURE);
136910506SBarry.Harding@Sun.COM }
137010506SBarry.Harding@Sun.COM 
137110506SBarry.Harding@Sun.COM static int
yge_register_port(yge_port_t * port)137210506SBarry.Harding@Sun.COM yge_register_port(yge_port_t *port)
137310506SBarry.Harding@Sun.COM {
137410506SBarry.Harding@Sun.COM 	if (mac_register(port->p_mreg, &port->p_mh) != DDI_SUCCESS) {
137510506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "MAC registration failed");
137610506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
137710506SBarry.Harding@Sun.COM 	}
137810506SBarry.Harding@Sun.COM 
137910506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
138010506SBarry.Harding@Sun.COM }
138110506SBarry.Harding@Sun.COM 
138210506SBarry.Harding@Sun.COM /*
138310506SBarry.Harding@Sun.COM  * Free up port specific resources. This is called only when the
138410506SBarry.Harding@Sun.COM  * port is not registered (and hence not running).
138510506SBarry.Harding@Sun.COM  */
138610506SBarry.Harding@Sun.COM static void
yge_uninit_port(yge_port_t * port)138710506SBarry.Harding@Sun.COM yge_uninit_port(yge_port_t *port)
138810506SBarry.Harding@Sun.COM {
138910506SBarry.Harding@Sun.COM 	ASSERT(!port->p_running);
139010506SBarry.Harding@Sun.COM 
139110506SBarry.Harding@Sun.COM 	if (port->p_mreg)
139210506SBarry.Harding@Sun.COM 		mac_free(port->p_mreg);
139310506SBarry.Harding@Sun.COM 
139410506SBarry.Harding@Sun.COM 	if (port->p_mii)
139510506SBarry.Harding@Sun.COM 		mii_free(port->p_mii);
139610506SBarry.Harding@Sun.COM 
139710506SBarry.Harding@Sun.COM 	yge_txrx_dma_free(port);
139810506SBarry.Harding@Sun.COM 
139910506SBarry.Harding@Sun.COM 	if (port->p_tx_buf)
140010506SBarry.Harding@Sun.COM 		kmem_free(port->p_tx_buf,
140110506SBarry.Harding@Sun.COM 		    sizeof (yge_buf_t) * YGE_TX_RING_CNT);
140210506SBarry.Harding@Sun.COM 	if (port->p_rx_buf)
140310506SBarry.Harding@Sun.COM 		kmem_free(port->p_rx_buf,
140410506SBarry.Harding@Sun.COM 		    sizeof (yge_buf_t) * YGE_RX_RING_CNT);
140510506SBarry.Harding@Sun.COM }
140610506SBarry.Harding@Sun.COM 
140710506SBarry.Harding@Sun.COM static void
yge_detach(yge_dev_t * dev)140810506SBarry.Harding@Sun.COM yge_detach(yge_dev_t *dev)
140910506SBarry.Harding@Sun.COM {
141010506SBarry.Harding@Sun.COM 	/*
141110506SBarry.Harding@Sun.COM 	 * Turn off the periodic.
141210506SBarry.Harding@Sun.COM 	 */
141310506SBarry.Harding@Sun.COM 	if (dev->d_periodic)
141410506SBarry.Harding@Sun.COM 		ddi_periodic_delete(dev->d_periodic);
141510506SBarry.Harding@Sun.COM 
141610506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
141710506SBarry.Harding@Sun.COM 		yge_uninit_port(dev->d_port[i]);
141810506SBarry.Harding@Sun.COM 	}
141910506SBarry.Harding@Sun.COM 
142010506SBarry.Harding@Sun.COM 	/*
142110506SBarry.Harding@Sun.COM 	 * Make sure all interrupts are disabled.
142210506SBarry.Harding@Sun.COM 	 */
142310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, 0);
142410506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
142510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
142610506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
142710506SBarry.Harding@Sun.COM 
142810506SBarry.Harding@Sun.COM 	/* LED Off. */
142910506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, Y2_LED_STAT_OFF);
143010506SBarry.Harding@Sun.COM 
143110506SBarry.Harding@Sun.COM 	/* Put hardware reset. */
143210506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
143310506SBarry.Harding@Sun.COM 
143410506SBarry.Harding@Sun.COM 	yge_free_ring(&dev->d_status_ring);
143510506SBarry.Harding@Sun.COM 
143610506SBarry.Harding@Sun.COM 	if (dev->d_task_q != NULL) {
143710506SBarry.Harding@Sun.COM 		yge_dispatch(dev, YGE_TASK_EXIT);
143810506SBarry.Harding@Sun.COM 		ddi_taskq_destroy(dev->d_task_q);
143910506SBarry.Harding@Sun.COM 		dev->d_task_q = NULL;
144010506SBarry.Harding@Sun.COM 	}
144110506SBarry.Harding@Sun.COM 
144210506SBarry.Harding@Sun.COM 	cv_destroy(&dev->d_task_cv);
144310506SBarry.Harding@Sun.COM 
144410506SBarry.Harding@Sun.COM 	yge_intr_disable(dev);
144510506SBarry.Harding@Sun.COM 
144610506SBarry.Harding@Sun.COM 	if (dev->d_intrh != NULL) {
144710506SBarry.Harding@Sun.COM 		for (int i = 0; i < dev->d_intrcnt; i++) {
144810506SBarry.Harding@Sun.COM 			(void) ddi_intr_remove_handler(dev->d_intrh[i]);
144910506SBarry.Harding@Sun.COM 			(void) ddi_intr_free(dev->d_intrh[i]);
145010506SBarry.Harding@Sun.COM 		}
145110506SBarry.Harding@Sun.COM 		kmem_free(dev->d_intrh, dev->d_intrsize);
145210506SBarry.Harding@Sun.COM 		mutex_destroy(&dev->d_phylock);
145310506SBarry.Harding@Sun.COM 		mutex_destroy(&dev->d_txlock);
145410506SBarry.Harding@Sun.COM 		mutex_destroy(&dev->d_rxlock);
145510506SBarry.Harding@Sun.COM 		mutex_destroy(&dev->d_task_mtx);
145610506SBarry.Harding@Sun.COM 	}
145710506SBarry.Harding@Sun.COM 	if (dev->d_regsh != NULL)
145810506SBarry.Harding@Sun.COM 		ddi_regs_map_free(&dev->d_regsh);
145910506SBarry.Harding@Sun.COM 
146010506SBarry.Harding@Sun.COM 	if (dev->d_pcih != NULL)
146110506SBarry.Harding@Sun.COM 		pci_config_teardown(&dev->d_pcih);
146210506SBarry.Harding@Sun.COM }
146310506SBarry.Harding@Sun.COM 
146410506SBarry.Harding@Sun.COM static int
yge_alloc_ring(yge_port_t * port,yge_dev_t * dev,yge_ring_t * ring,uint32_t num)146510506SBarry.Harding@Sun.COM yge_alloc_ring(yge_port_t *port, yge_dev_t *dev, yge_ring_t *ring, uint32_t num)
146610506SBarry.Harding@Sun.COM {
146710506SBarry.Harding@Sun.COM 	dev_info_t		*dip;
146810506SBarry.Harding@Sun.COM 	caddr_t			kaddr;
146910506SBarry.Harding@Sun.COM 	size_t			len;
147010506SBarry.Harding@Sun.COM 	int			rv;
147110506SBarry.Harding@Sun.COM 	ddi_dma_cookie_t	dmac;
147210506SBarry.Harding@Sun.COM 	unsigned		ndmac;
147310506SBarry.Harding@Sun.COM 
147410506SBarry.Harding@Sun.COM 	if (port && !dev)
147510506SBarry.Harding@Sun.COM 		dev = port->p_dev;
147610506SBarry.Harding@Sun.COM 	dip = dev->d_dip;
147710506SBarry.Harding@Sun.COM 
147810506SBarry.Harding@Sun.COM 	ring->r_num = num;
147910506SBarry.Harding@Sun.COM 
148010506SBarry.Harding@Sun.COM 	rv = ddi_dma_alloc_handle(dip, &yge_ring_dma_attr, DDI_DMA_DONTWAIT,
148110506SBarry.Harding@Sun.COM 	    NULL, &ring->r_dmah);
148210506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
148310506SBarry.Harding@Sun.COM 		yge_error(dev, port, "Unable to allocate ring DMA handle");
148410506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
148510506SBarry.Harding@Sun.COM 	}
148610506SBarry.Harding@Sun.COM 
148710506SBarry.Harding@Sun.COM 	rv = ddi_dma_mem_alloc(ring->r_dmah, num * sizeof (yge_desc_t),
148810506SBarry.Harding@Sun.COM 	    &yge_ring_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
148910506SBarry.Harding@Sun.COM 	    &kaddr, &len, &ring->r_acch);
149010506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
149110506SBarry.Harding@Sun.COM 		yge_error(dev, port, "Unable to allocate ring DMA memory");
149210506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
149310506SBarry.Harding@Sun.COM 	}
149410506SBarry.Harding@Sun.COM 	ring->r_size = len;
149510506SBarry.Harding@Sun.COM 	ring->r_kaddr = (void *)kaddr;
149610506SBarry.Harding@Sun.COM 
149710506SBarry.Harding@Sun.COM 	bzero(kaddr, len);
149810506SBarry.Harding@Sun.COM 
149910506SBarry.Harding@Sun.COM 	rv = ddi_dma_addr_bind_handle(ring->r_dmah, NULL, kaddr,
150010506SBarry.Harding@Sun.COM 	    len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
150110506SBarry.Harding@Sun.COM 	    &dmac, &ndmac);
150210506SBarry.Harding@Sun.COM 	if (rv != DDI_DMA_MAPPED) {
150310506SBarry.Harding@Sun.COM 		yge_error(dev, port, "Unable to bind ring DMA handle");
150410506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
150510506SBarry.Harding@Sun.COM 	}
150610506SBarry.Harding@Sun.COM 	ASSERT(ndmac == 1);
150710506SBarry.Harding@Sun.COM 	ring->r_paddr = dmac.dmac_address;
150810506SBarry.Harding@Sun.COM 
150910506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
151010506SBarry.Harding@Sun.COM }
151110506SBarry.Harding@Sun.COM 
151210506SBarry.Harding@Sun.COM static void
yge_free_ring(yge_ring_t * ring)151310506SBarry.Harding@Sun.COM yge_free_ring(yge_ring_t *ring)
151410506SBarry.Harding@Sun.COM {
151510506SBarry.Harding@Sun.COM 	if (ring->r_paddr)
151610506SBarry.Harding@Sun.COM 		(void) ddi_dma_unbind_handle(ring->r_dmah);
151710506SBarry.Harding@Sun.COM 	ring->r_paddr = 0;
151810506SBarry.Harding@Sun.COM 	if (ring->r_acch)
151910506SBarry.Harding@Sun.COM 		ddi_dma_mem_free(&ring->r_acch);
152010506SBarry.Harding@Sun.COM 	ring->r_kaddr = NULL;
152110506SBarry.Harding@Sun.COM 	ring->r_acch = NULL;
152210506SBarry.Harding@Sun.COM 	if (ring->r_dmah)
152310506SBarry.Harding@Sun.COM 		ddi_dma_free_handle(&ring->r_dmah);
152410506SBarry.Harding@Sun.COM 	ring->r_dmah = NULL;
152510506SBarry.Harding@Sun.COM }
152610506SBarry.Harding@Sun.COM 
152710506SBarry.Harding@Sun.COM static int
yge_alloc_buf(yge_port_t * port,yge_buf_t * b,size_t bufsz,int flag)152810506SBarry.Harding@Sun.COM yge_alloc_buf(yge_port_t *port, yge_buf_t *b, size_t bufsz, int flag)
152910506SBarry.Harding@Sun.COM {
153010506SBarry.Harding@Sun.COM 	yge_dev_t	*dev = port->p_dev;
153110506SBarry.Harding@Sun.COM 	size_t		l;
153210506SBarry.Harding@Sun.COM 	int		sflag;
153310506SBarry.Harding@Sun.COM 	int 		rv;
153410506SBarry.Harding@Sun.COM 	ddi_dma_cookie_t	dmac;
153510506SBarry.Harding@Sun.COM 	unsigned		ndmac;
153610506SBarry.Harding@Sun.COM 
153710506SBarry.Harding@Sun.COM 	sflag = flag & (DDI_DMA_STREAMING | DDI_DMA_CONSISTENT);
153810506SBarry.Harding@Sun.COM 
153910506SBarry.Harding@Sun.COM 	/* Now allocate Tx buffers. */
154010506SBarry.Harding@Sun.COM 	rv = ddi_dma_alloc_handle(dev->d_dip, &yge_buf_dma_attr,
154110506SBarry.Harding@Sun.COM 	    DDI_DMA_DONTWAIT, NULL, &b->b_dmah);
154210506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
154310506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Unable to alloc DMA handle for buffer");
154410506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
154510506SBarry.Harding@Sun.COM 	}
154610506SBarry.Harding@Sun.COM 
154710506SBarry.Harding@Sun.COM 	rv = ddi_dma_mem_alloc(b->b_dmah, bufsz, &yge_buf_attr,
154810506SBarry.Harding@Sun.COM 	    sflag, DDI_DMA_DONTWAIT, NULL, &b->b_buf, &l, &b->b_acch);
154910506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
155010506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Unable to alloc DMA memory for buffer");
155110506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
155210506SBarry.Harding@Sun.COM 	}
155310506SBarry.Harding@Sun.COM 
155410506SBarry.Harding@Sun.COM 	rv = ddi_dma_addr_bind_handle(b->b_dmah, NULL, b->b_buf, l, flag,
155510506SBarry.Harding@Sun.COM 	    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
155610506SBarry.Harding@Sun.COM 	if (rv != DDI_DMA_MAPPED) {
155710506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Unable to bind DMA handle for buffer");
155810506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
155910506SBarry.Harding@Sun.COM 	}
156010506SBarry.Harding@Sun.COM 	ASSERT(ndmac == 1);
156110506SBarry.Harding@Sun.COM 	b->b_paddr = dmac.dmac_address;
156210506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
156310506SBarry.Harding@Sun.COM }
156410506SBarry.Harding@Sun.COM 
156510506SBarry.Harding@Sun.COM static void
yge_free_buf(yge_buf_t * b)156610506SBarry.Harding@Sun.COM yge_free_buf(yge_buf_t *b)
156710506SBarry.Harding@Sun.COM {
156810506SBarry.Harding@Sun.COM 	if (b->b_paddr)
156910506SBarry.Harding@Sun.COM 		(void) ddi_dma_unbind_handle(b->b_dmah);
157010506SBarry.Harding@Sun.COM 	b->b_paddr = 0;
157110506SBarry.Harding@Sun.COM 	if (b->b_acch)
157210506SBarry.Harding@Sun.COM 		ddi_dma_mem_free(&b->b_acch);
157310506SBarry.Harding@Sun.COM 	b->b_buf = NULL;
157410506SBarry.Harding@Sun.COM 	b->b_acch = NULL;
157510506SBarry.Harding@Sun.COM 	if (b->b_dmah)
157610506SBarry.Harding@Sun.COM 		ddi_dma_free_handle(&b->b_dmah);
157710506SBarry.Harding@Sun.COM 	b->b_dmah = NULL;
157810506SBarry.Harding@Sun.COM }
157910506SBarry.Harding@Sun.COM 
158010506SBarry.Harding@Sun.COM static int
yge_txrx_dma_alloc(yge_port_t * port)158110506SBarry.Harding@Sun.COM yge_txrx_dma_alloc(yge_port_t *port)
158210506SBarry.Harding@Sun.COM {
158310506SBarry.Harding@Sun.COM 	uint32_t		bufsz;
158410506SBarry.Harding@Sun.COM 	int			rv;
158510506SBarry.Harding@Sun.COM 	int			i;
158610506SBarry.Harding@Sun.COM 	yge_buf_t		*b;
158710506SBarry.Harding@Sun.COM 
158810506SBarry.Harding@Sun.COM 	/*
158910506SBarry.Harding@Sun.COM 	 * It seems that Yukon II supports full 64 bit DMA operations.
159010506SBarry.Harding@Sun.COM 	 * But we limit it to 32 bits only for now.  The 64 bit
159110506SBarry.Harding@Sun.COM 	 * operation would require substantially more complex
159210506SBarry.Harding@Sun.COM 	 * descriptor handling, since in such a case we would need two
159310506SBarry.Harding@Sun.COM 	 * LEs to represent a single physical address.
159410506SBarry.Harding@Sun.COM 	 *
159510506SBarry.Harding@Sun.COM 	 * If we find that this is limiting us, then we should go back
159610506SBarry.Harding@Sun.COM 	 * and re-examine it.
159710506SBarry.Harding@Sun.COM 	 */
159810506SBarry.Harding@Sun.COM 
159910506SBarry.Harding@Sun.COM 	/* Note our preferred buffer size. */
160010506SBarry.Harding@Sun.COM 	bufsz = port->p_mtu;
160110506SBarry.Harding@Sun.COM 
160210506SBarry.Harding@Sun.COM 	/* Allocate Tx ring. */
160310506SBarry.Harding@Sun.COM 	rv = yge_alloc_ring(port, NULL, &port->p_tx_ring, YGE_TX_RING_CNT);
160410506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
160510506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
160610506SBarry.Harding@Sun.COM 	}
160710506SBarry.Harding@Sun.COM 
160810506SBarry.Harding@Sun.COM 	/* Now allocate Tx buffers. */
160910506SBarry.Harding@Sun.COM 	b = port->p_tx_buf;
161010506SBarry.Harding@Sun.COM 	for (i = 0; i < YGE_TX_RING_CNT; i++) {
161110506SBarry.Harding@Sun.COM 		rv = yge_alloc_buf(port, b, bufsz,
161210506SBarry.Harding@Sun.COM 		    DDI_DMA_STREAMING | DDI_DMA_WRITE);
161310506SBarry.Harding@Sun.COM 		if (rv != DDI_SUCCESS) {
161410506SBarry.Harding@Sun.COM 			return (DDI_FAILURE);
161510506SBarry.Harding@Sun.COM 		}
161610506SBarry.Harding@Sun.COM 		b++;
161710506SBarry.Harding@Sun.COM 	}
161810506SBarry.Harding@Sun.COM 
161910506SBarry.Harding@Sun.COM 	/* Allocate Rx ring. */
162010506SBarry.Harding@Sun.COM 	rv = yge_alloc_ring(port, NULL, &port->p_rx_ring, YGE_RX_RING_CNT);
162110506SBarry.Harding@Sun.COM 	if (rv != DDI_SUCCESS) {
162210506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
162310506SBarry.Harding@Sun.COM 	}
162410506SBarry.Harding@Sun.COM 
162510506SBarry.Harding@Sun.COM 	/* Now allocate Rx buffers. */
162610506SBarry.Harding@Sun.COM 	b = port->p_rx_buf;
162710506SBarry.Harding@Sun.COM 	for (i = 0; i < YGE_RX_RING_CNT; i++) {
162810506SBarry.Harding@Sun.COM 		rv =  yge_alloc_buf(port, b, bufsz,
162910506SBarry.Harding@Sun.COM 		    DDI_DMA_STREAMING | DDI_DMA_READ);
163010506SBarry.Harding@Sun.COM 		if (rv != DDI_SUCCESS) {
163110506SBarry.Harding@Sun.COM 			return (DDI_FAILURE);
163210506SBarry.Harding@Sun.COM 		}
163310506SBarry.Harding@Sun.COM 		b++;
163410506SBarry.Harding@Sun.COM 	}
163510506SBarry.Harding@Sun.COM 
163610506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
163710506SBarry.Harding@Sun.COM }
163810506SBarry.Harding@Sun.COM 
163910506SBarry.Harding@Sun.COM static void
yge_txrx_dma_free(yge_port_t * port)164010506SBarry.Harding@Sun.COM yge_txrx_dma_free(yge_port_t *port)
164110506SBarry.Harding@Sun.COM {
164210506SBarry.Harding@Sun.COM 	yge_buf_t	*b;
164310506SBarry.Harding@Sun.COM 
164410506SBarry.Harding@Sun.COM 	/* Tx ring. */
164510506SBarry.Harding@Sun.COM 	yge_free_ring(&port->p_tx_ring);
164610506SBarry.Harding@Sun.COM 
164710506SBarry.Harding@Sun.COM 	/* Rx ring. */
164810506SBarry.Harding@Sun.COM 	yge_free_ring(&port->p_rx_ring);
164910506SBarry.Harding@Sun.COM 
165010506SBarry.Harding@Sun.COM 	/* Tx buffers. */
165110506SBarry.Harding@Sun.COM 	b = port->p_tx_buf;
165210506SBarry.Harding@Sun.COM 	for (int i = 0; i < YGE_TX_RING_CNT; i++, b++) {
165310506SBarry.Harding@Sun.COM 		yge_free_buf(b);
165410506SBarry.Harding@Sun.COM 	}
165510506SBarry.Harding@Sun.COM 	/* Rx buffers. */
165610506SBarry.Harding@Sun.COM 	b = port->p_rx_buf;
165710506SBarry.Harding@Sun.COM 	for (int i = 0; i < YGE_RX_RING_CNT; i++, b++) {
165810506SBarry.Harding@Sun.COM 		yge_free_buf(b);
165910506SBarry.Harding@Sun.COM 	}
166010506SBarry.Harding@Sun.COM }
166110506SBarry.Harding@Sun.COM 
166210506SBarry.Harding@Sun.COM boolean_t
yge_send(yge_port_t * port,mblk_t * mp)166310506SBarry.Harding@Sun.COM yge_send(yge_port_t *port, mblk_t *mp)
166410506SBarry.Harding@Sun.COM {
166510506SBarry.Harding@Sun.COM 	yge_ring_t *ring = &port->p_tx_ring;
166610506SBarry.Harding@Sun.COM 	yge_buf_t *txb;
166710506SBarry.Harding@Sun.COM 	int16_t prod;
166810506SBarry.Harding@Sun.COM 	size_t len;
166910506SBarry.Harding@Sun.COM 
167010506SBarry.Harding@Sun.COM 	/*
167110506SBarry.Harding@Sun.COM 	 * For now we're not going to support checksum offload or LSO.
167210506SBarry.Harding@Sun.COM 	 */
167310506SBarry.Harding@Sun.COM 
167410506SBarry.Harding@Sun.COM 	len = msgsize(mp);
167510506SBarry.Harding@Sun.COM 	if (len > port->p_framesize) {
167610506SBarry.Harding@Sun.COM 		/* too big! */
167710506SBarry.Harding@Sun.COM 		freemsg(mp);
167810506SBarry.Harding@Sun.COM 		return (B_TRUE);
167910506SBarry.Harding@Sun.COM 	}
168010506SBarry.Harding@Sun.COM 
168110506SBarry.Harding@Sun.COM 	/* Check number of available descriptors. */
168210506SBarry.Harding@Sun.COM 	if (port->p_tx_cnt + 1 >=
168310506SBarry.Harding@Sun.COM 	    (YGE_TX_RING_CNT - YGE_RESERVED_TX_DESC_CNT)) {
168410506SBarry.Harding@Sun.COM 		port->p_wantw = B_TRUE;
168510506SBarry.Harding@Sun.COM 		return (B_FALSE);
168610506SBarry.Harding@Sun.COM 	}
168710506SBarry.Harding@Sun.COM 
168810506SBarry.Harding@Sun.COM 	prod = port->p_tx_prod;
168910506SBarry.Harding@Sun.COM 
169010506SBarry.Harding@Sun.COM 	txb = &port->p_tx_buf[prod];
169110506SBarry.Harding@Sun.COM 	mcopymsg(mp, txb->b_buf);
169210506SBarry.Harding@Sun.COM 	SYNCBUF(txb, DDI_DMA_SYNC_FORDEV);
169310506SBarry.Harding@Sun.COM 
169410506SBarry.Harding@Sun.COM 	PUTADDR(ring, prod, txb->b_paddr);
169510506SBarry.Harding@Sun.COM 	PUTCTRL(ring, prod, len | OP_PACKET | HW_OWNER | EOP);
169610506SBarry.Harding@Sun.COM 	SYNCENTRY(ring, prod, DDI_DMA_SYNC_FORDEV);
169710506SBarry.Harding@Sun.COM 	port->p_tx_cnt++;
169810506SBarry.Harding@Sun.COM 
169910506SBarry.Harding@Sun.COM 	YGE_INC(prod, YGE_TX_RING_CNT);
170010506SBarry.Harding@Sun.COM 
170110506SBarry.Harding@Sun.COM 	/* Update producer index. */
170210506SBarry.Harding@Sun.COM 	port->p_tx_prod = prod;
170310506SBarry.Harding@Sun.COM 
170410506SBarry.Harding@Sun.COM 	return (B_TRUE);
170510506SBarry.Harding@Sun.COM }
170610506SBarry.Harding@Sun.COM 
170710506SBarry.Harding@Sun.COM static int
yge_suspend(yge_dev_t * dev)170810506SBarry.Harding@Sun.COM yge_suspend(yge_dev_t *dev)
170910506SBarry.Harding@Sun.COM {
171010506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
171110506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
171210506SBarry.Harding@Sun.COM 		mii_suspend(port->p_mii);
171310506SBarry.Harding@Sun.COM 	}
171410506SBarry.Harding@Sun.COM 
171510506SBarry.Harding@Sun.COM 
171610506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
171710506SBarry.Harding@Sun.COM 
171810506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
171910506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
172010506SBarry.Harding@Sun.COM 
172110506SBarry.Harding@Sun.COM 		if (port->p_running) {
172210506SBarry.Harding@Sun.COM 			yge_stop_port(port);
172310506SBarry.Harding@Sun.COM 		}
172410506SBarry.Harding@Sun.COM 	}
172510506SBarry.Harding@Sun.COM 
172610506SBarry.Harding@Sun.COM 	/* Disable all interrupts. */
172710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, 0);
172810506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
172910506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
173010506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
173110506SBarry.Harding@Sun.COM 
173210506SBarry.Harding@Sun.COM 	yge_phy_power(dev, B_FALSE);
173310506SBarry.Harding@Sun.COM 
173410506SBarry.Harding@Sun.COM 	/* Put hardware reset. */
173510506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
173610506SBarry.Harding@Sun.COM 	dev->d_suspended = B_TRUE;
173710506SBarry.Harding@Sun.COM 
173810506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
173910506SBarry.Harding@Sun.COM 
174010506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
174110506SBarry.Harding@Sun.COM }
174210506SBarry.Harding@Sun.COM 
174310506SBarry.Harding@Sun.COM static int
yge_resume(yge_dev_t * dev)174410506SBarry.Harding@Sun.COM yge_resume(yge_dev_t *dev)
174510506SBarry.Harding@Sun.COM {
174610506SBarry.Harding@Sun.COM 	uint8_t pm_cap;
174710506SBarry.Harding@Sun.COM 
174810506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
174910506SBarry.Harding@Sun.COM 
175010506SBarry.Harding@Sun.COM 	/* ensure the pmcsr status is D0 state */
175110506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
175210506SBarry.Harding@Sun.COM 
175310506SBarry.Harding@Sun.COM 	if ((pm_cap = yge_find_capability(dev, PCI_CAP_ID_PM)) != 0) {
175410506SBarry.Harding@Sun.COM 		uint16_t pmcsr;
175510506SBarry.Harding@Sun.COM 		pmcsr = pci_config_get16(dev->d_pcih, pm_cap + PCI_PMCSR);
175610506SBarry.Harding@Sun.COM 		pmcsr &= ~PCI_PMCSR_STATE_MASK;
175710506SBarry.Harding@Sun.COM 		pci_config_put16(dev->d_pcih, pm_cap + PCI_PMCSR,
175810506SBarry.Harding@Sun.COM 		    pmcsr | PCI_PMCSR_D0);
175910506SBarry.Harding@Sun.COM 	}
176010506SBarry.Harding@Sun.COM 
176110506SBarry.Harding@Sun.COM 	/* Enable PCI access and bus master. */
176210506SBarry.Harding@Sun.COM 	pci_config_put16(dev->d_pcih, PCI_CONF_COMM,
176310506SBarry.Harding@Sun.COM 	    pci_config_get16(dev->d_pcih, PCI_CONF_COMM) |
176410506SBarry.Harding@Sun.COM 	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
176510506SBarry.Harding@Sun.COM 
176610506SBarry.Harding@Sun.COM 	/* Enable all clocks. */
176710506SBarry.Harding@Sun.COM 	switch (dev->d_hw_id) {
176810506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EX:
176910506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EC_U:
177010506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_FE_P:
177110506SBarry.Harding@Sun.COM 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
177210506SBarry.Harding@Sun.COM 		break;
177310506SBarry.Harding@Sun.COM 	}
177410506SBarry.Harding@Sun.COM 
177510506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
177610506SBarry.Harding@Sun.COM 
177710506SBarry.Harding@Sun.COM 	yge_reset(dev);
177810506SBarry.Harding@Sun.COM 
177910506SBarry.Harding@Sun.COM 	/* Make sure interrupts are reenabled */
178010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, 0);
178110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, Y2_IS_HW_ERR | Y2_IS_STAT_BMU);
178210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK,
178310506SBarry.Harding@Sun.COM 	    Y2_IS_TIST_OV | Y2_IS_MST_ERR |
178410506SBarry.Harding@Sun.COM 	    Y2_IS_IRQ_STAT | Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP);
178510506SBarry.Harding@Sun.COM 
178610506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
178710506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
178810506SBarry.Harding@Sun.COM 
178910506SBarry.Harding@Sun.COM 		if (port != NULL && port->p_running) {
179010506SBarry.Harding@Sun.COM 			yge_start_port(port);
179110506SBarry.Harding@Sun.COM 		}
179210506SBarry.Harding@Sun.COM 	}
179310506SBarry.Harding@Sun.COM 	dev->d_suspended = B_FALSE;
179410506SBarry.Harding@Sun.COM 
179510506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
179610506SBarry.Harding@Sun.COM 
179710506SBarry.Harding@Sun.COM 	/* Reset MII layer */
179810506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
179910506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
180010506SBarry.Harding@Sun.COM 
180110506SBarry.Harding@Sun.COM 		if (port->p_running) {
180210506SBarry.Harding@Sun.COM 			mii_resume(port->p_mii);
180310506SBarry.Harding@Sun.COM 			mac_tx_update(port->p_mh);
180410506SBarry.Harding@Sun.COM 		}
180510506SBarry.Harding@Sun.COM 	}
180610506SBarry.Harding@Sun.COM 
180710506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
180810506SBarry.Harding@Sun.COM }
180910506SBarry.Harding@Sun.COM 
181010506SBarry.Harding@Sun.COM static mblk_t *
yge_rxeof(yge_port_t * port,uint32_t status,int len)181110506SBarry.Harding@Sun.COM yge_rxeof(yge_port_t *port, uint32_t status, int len)
181210506SBarry.Harding@Sun.COM {
181310506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
181410506SBarry.Harding@Sun.COM 	mblk_t	*mp;
181510506SBarry.Harding@Sun.COM 	int cons, rxlen;
181610506SBarry.Harding@Sun.COM 	yge_buf_t *rxb;
181710506SBarry.Harding@Sun.COM 	yge_ring_t *ring;
181810506SBarry.Harding@Sun.COM 
181910506SBarry.Harding@Sun.COM 	ASSERT(mutex_owned(&dev->d_rxlock));
182010506SBarry.Harding@Sun.COM 
182110506SBarry.Harding@Sun.COM 	if (!port->p_running)
182210506SBarry.Harding@Sun.COM 		return (NULL);
182310506SBarry.Harding@Sun.COM 
182410506SBarry.Harding@Sun.COM 	ring = &port->p_rx_ring;
182510506SBarry.Harding@Sun.COM 	cons = port->p_rx_cons;
182610506SBarry.Harding@Sun.COM 	rxlen = status >> 16;
182710506SBarry.Harding@Sun.COM 	rxb = &port->p_rx_buf[cons];
182810506SBarry.Harding@Sun.COM 	mp = NULL;
182910506SBarry.Harding@Sun.COM 
183010506SBarry.Harding@Sun.COM 
183110506SBarry.Harding@Sun.COM 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
183210506SBarry.Harding@Sun.COM 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
183310506SBarry.Harding@Sun.COM 		/*
183410506SBarry.Harding@Sun.COM 		 * Apparently the status for this chip is not reliable.
183510506SBarry.Harding@Sun.COM 		 * Only perform minimal consistency checking; the MAC
183610506SBarry.Harding@Sun.COM 		 * and upper protocols will have to filter any garbage.
183710506SBarry.Harding@Sun.COM 		 */
183810506SBarry.Harding@Sun.COM 		if ((len > port->p_framesize) || (rxlen != len)) {
183910506SBarry.Harding@Sun.COM 			goto bad;
184010506SBarry.Harding@Sun.COM 		}
184110506SBarry.Harding@Sun.COM 	} else {
184210506SBarry.Harding@Sun.COM 		if ((len > port->p_framesize) || (rxlen != len) ||
184310506SBarry.Harding@Sun.COM 		    ((status & GMR_FS_ANY_ERR) != 0) ||
184410506SBarry.Harding@Sun.COM 		    ((status & GMR_FS_RX_OK) == 0)) {
184510506SBarry.Harding@Sun.COM 			goto bad;
184610506SBarry.Harding@Sun.COM 		}
184710506SBarry.Harding@Sun.COM 	}
184810506SBarry.Harding@Sun.COM 
184910506SBarry.Harding@Sun.COM 	if ((mp = allocb(len + YGE_HEADROOM, BPRI_HI)) != NULL) {
185010506SBarry.Harding@Sun.COM 
185110506SBarry.Harding@Sun.COM 		/* good packet - yay */
185210506SBarry.Harding@Sun.COM 		mp->b_rptr += YGE_HEADROOM;
185310506SBarry.Harding@Sun.COM 		SYNCBUF(rxb, DDI_DMA_SYNC_FORKERNEL);
185410506SBarry.Harding@Sun.COM 		bcopy(rxb->b_buf, mp->b_rptr, len);
185510506SBarry.Harding@Sun.COM 		mp->b_wptr = mp->b_rptr + len;
185610506SBarry.Harding@Sun.COM 	} else {
185710506SBarry.Harding@Sun.COM 		port->p_stats.rx_nobuf++;
185810506SBarry.Harding@Sun.COM 	}
185910506SBarry.Harding@Sun.COM 
186010506SBarry.Harding@Sun.COM bad:
186110506SBarry.Harding@Sun.COM 
186210506SBarry.Harding@Sun.COM 	PUTCTRL(ring, cons, port->p_framesize | OP_PACKET | HW_OWNER);
186310506SBarry.Harding@Sun.COM 	SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORDEV);
186410506SBarry.Harding@Sun.COM 
186510506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev,
186610506SBarry.Harding@Sun.COM 	    Y2_PREF_Q_ADDR(port->p_rxq, PREF_UNIT_PUT_IDX_REG),
186710506SBarry.Harding@Sun.COM 	    cons);
186810506SBarry.Harding@Sun.COM 
186910506SBarry.Harding@Sun.COM 	YGE_INC(port->p_rx_cons, YGE_RX_RING_CNT);
187010506SBarry.Harding@Sun.COM 
187110506SBarry.Harding@Sun.COM 	return (mp);
187210506SBarry.Harding@Sun.COM }
187310506SBarry.Harding@Sun.COM 
187410506SBarry.Harding@Sun.COM static boolean_t
yge_txeof_locked(yge_port_t * port,int idx)187510506SBarry.Harding@Sun.COM yge_txeof_locked(yge_port_t *port, int idx)
187610506SBarry.Harding@Sun.COM {
187710506SBarry.Harding@Sun.COM 	int prog;
187810506SBarry.Harding@Sun.COM 	int16_t cons;
187910506SBarry.Harding@Sun.COM 	boolean_t resched;
188010506SBarry.Harding@Sun.COM 
188110506SBarry.Harding@Sun.COM 	if (!port->p_running) {
188210506SBarry.Harding@Sun.COM 		return (B_FALSE);
188310506SBarry.Harding@Sun.COM 	}
188410506SBarry.Harding@Sun.COM 
188510506SBarry.Harding@Sun.COM 	cons = port->p_tx_cons;
188610506SBarry.Harding@Sun.COM 	prog = 0;
188710506SBarry.Harding@Sun.COM 	for (; cons != idx; YGE_INC(cons, YGE_TX_RING_CNT)) {
188810506SBarry.Harding@Sun.COM 		if (port->p_tx_cnt <= 0)
188910506SBarry.Harding@Sun.COM 			break;
189010506SBarry.Harding@Sun.COM 		prog++;
189110506SBarry.Harding@Sun.COM 		port->p_tx_cnt--;
189210506SBarry.Harding@Sun.COM 		/* No need to sync LEs as we didn't update LEs. */
189310506SBarry.Harding@Sun.COM 	}
189410506SBarry.Harding@Sun.COM 
189510506SBarry.Harding@Sun.COM 	port->p_tx_cons = cons;
189610506SBarry.Harding@Sun.COM 
189710506SBarry.Harding@Sun.COM 	if (prog > 0) {
189810506SBarry.Harding@Sun.COM 		resched = port->p_wantw;
189910506SBarry.Harding@Sun.COM 		port->p_tx_wdog = 0;
190010506SBarry.Harding@Sun.COM 		port->p_wantw = B_FALSE;
190110506SBarry.Harding@Sun.COM 		return (resched);
190210506SBarry.Harding@Sun.COM 	} else {
190310506SBarry.Harding@Sun.COM 		return (B_FALSE);
190410506SBarry.Harding@Sun.COM 	}
190510506SBarry.Harding@Sun.COM }
190610506SBarry.Harding@Sun.COM 
190710506SBarry.Harding@Sun.COM static void
yge_txeof(yge_port_t * port,int idx)190810506SBarry.Harding@Sun.COM yge_txeof(yge_port_t *port, int idx)
190910506SBarry.Harding@Sun.COM {
191010506SBarry.Harding@Sun.COM 	boolean_t resched;
191110506SBarry.Harding@Sun.COM 
191210506SBarry.Harding@Sun.COM 	TX_LOCK(port->p_dev);
191310506SBarry.Harding@Sun.COM 
191410506SBarry.Harding@Sun.COM 	resched = yge_txeof_locked(port, idx);
191510506SBarry.Harding@Sun.COM 
191610506SBarry.Harding@Sun.COM 	TX_UNLOCK(port->p_dev);
191710506SBarry.Harding@Sun.COM 
191810506SBarry.Harding@Sun.COM 	if (resched && port->p_running) {
191910506SBarry.Harding@Sun.COM 		mac_tx_update(port->p_mh);
192010506SBarry.Harding@Sun.COM 	}
192110506SBarry.Harding@Sun.COM }
192210506SBarry.Harding@Sun.COM 
192310506SBarry.Harding@Sun.COM static void
yge_restart_task(yge_dev_t * dev)192410506SBarry.Harding@Sun.COM yge_restart_task(yge_dev_t *dev)
192510506SBarry.Harding@Sun.COM {
192610506SBarry.Harding@Sun.COM 	yge_port_t *port;
192710506SBarry.Harding@Sun.COM 
192810506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
192910506SBarry.Harding@Sun.COM 
193010506SBarry.Harding@Sun.COM 	/* Cancel pending I/O and free all Rx/Tx buffers. */
193110506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
193210506SBarry.Harding@Sun.COM 		port = dev->d_port[i];
193310506SBarry.Harding@Sun.COM 		if (port->p_running)
193410506SBarry.Harding@Sun.COM 			yge_stop_port(dev->d_port[i]);
193510506SBarry.Harding@Sun.COM 	}
193610506SBarry.Harding@Sun.COM 	yge_reset(dev);
193710506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
193810506SBarry.Harding@Sun.COM 		port = dev->d_port[i];
193910506SBarry.Harding@Sun.COM 
194010506SBarry.Harding@Sun.COM 		if (port->p_running)
194110506SBarry.Harding@Sun.COM 			yge_start_port(port);
194210506SBarry.Harding@Sun.COM 	}
194310506SBarry.Harding@Sun.COM 
194410506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
194510506SBarry.Harding@Sun.COM 
194610506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
194710506SBarry.Harding@Sun.COM 		port = dev->d_port[i];
194810506SBarry.Harding@Sun.COM 
194910506SBarry.Harding@Sun.COM 		mii_reset(port->p_mii);
195010506SBarry.Harding@Sun.COM 		if (port->p_running)
195110506SBarry.Harding@Sun.COM 			mac_tx_update(port->p_mh);
195210506SBarry.Harding@Sun.COM 	}
195310506SBarry.Harding@Sun.COM }
195410506SBarry.Harding@Sun.COM 
195510506SBarry.Harding@Sun.COM static void
yge_tick(void * arg)195610506SBarry.Harding@Sun.COM yge_tick(void *arg)
195710506SBarry.Harding@Sun.COM {
195810506SBarry.Harding@Sun.COM 	yge_dev_t *dev = arg;
195910506SBarry.Harding@Sun.COM 	yge_port_t *port;
196010506SBarry.Harding@Sun.COM 	boolean_t restart = B_FALSE;
196110506SBarry.Harding@Sun.COM 	boolean_t resched = B_FALSE;
196210506SBarry.Harding@Sun.COM 	int idx;
196310506SBarry.Harding@Sun.COM 
196410506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
196510506SBarry.Harding@Sun.COM 
196610506SBarry.Harding@Sun.COM 	if (dev->d_suspended) {
196710506SBarry.Harding@Sun.COM 		DEV_UNLOCK(dev);
196810506SBarry.Harding@Sun.COM 		return;
196910506SBarry.Harding@Sun.COM 	}
197010506SBarry.Harding@Sun.COM 
197110506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
197210506SBarry.Harding@Sun.COM 		port = dev->d_port[i];
197310506SBarry.Harding@Sun.COM 
197410506SBarry.Harding@Sun.COM 		if (!port->p_running)
197510506SBarry.Harding@Sun.COM 			continue;
197610506SBarry.Harding@Sun.COM 
197710506SBarry.Harding@Sun.COM 		if (port->p_tx_cnt) {
197810506SBarry.Harding@Sun.COM 			uint32_t ridx;
197910506SBarry.Harding@Sun.COM 
198010506SBarry.Harding@Sun.COM 			/*
198110506SBarry.Harding@Sun.COM 			 * Reclaim first as there is a possibility of losing
198210506SBarry.Harding@Sun.COM 			 * Tx completion interrupts.
198310506SBarry.Harding@Sun.COM 			 */
198410506SBarry.Harding@Sun.COM 			ridx = port->p_port == YGE_PORT_A ?
198510506SBarry.Harding@Sun.COM 			    STAT_TXA1_RIDX : STAT_TXA2_RIDX;
198610506SBarry.Harding@Sun.COM 			idx = CSR_READ_2(dev, ridx);
198710506SBarry.Harding@Sun.COM 			if (port->p_tx_cons != idx) {
198810506SBarry.Harding@Sun.COM 				resched = yge_txeof_locked(port, idx);
198910506SBarry.Harding@Sun.COM 
199010506SBarry.Harding@Sun.COM 			} else {
199110506SBarry.Harding@Sun.COM 
199210506SBarry.Harding@Sun.COM 				/* detect TX hang */
199310506SBarry.Harding@Sun.COM 				port->p_tx_wdog++;
199410506SBarry.Harding@Sun.COM 				if (port->p_tx_wdog > YGE_TX_TIMEOUT) {
199510506SBarry.Harding@Sun.COM 					port->p_tx_wdog = 0;
199610506SBarry.Harding@Sun.COM 					yge_error(NULL, port,
199710506SBarry.Harding@Sun.COM 					    "TX hang detected!");
199810506SBarry.Harding@Sun.COM 					restart = B_TRUE;
199910506SBarry.Harding@Sun.COM 				}
200010506SBarry.Harding@Sun.COM 			}
200110506SBarry.Harding@Sun.COM 		}
200210506SBarry.Harding@Sun.COM 	}
200310506SBarry.Harding@Sun.COM 
200410506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
200510506SBarry.Harding@Sun.COM 	if (restart) {
200610506SBarry.Harding@Sun.COM 		yge_dispatch(dev, YGE_TASK_RESTART);
200710506SBarry.Harding@Sun.COM 	} else {
200810506SBarry.Harding@Sun.COM 		if (resched) {
200910506SBarry.Harding@Sun.COM 			for (int i = 0; i < dev->d_num_port; i++) {
201010506SBarry.Harding@Sun.COM 				port = dev->d_port[i];
201110506SBarry.Harding@Sun.COM 
201210506SBarry.Harding@Sun.COM 				if (port->p_running)
201310506SBarry.Harding@Sun.COM 					mac_tx_update(port->p_mh);
201410506SBarry.Harding@Sun.COM 			}
201510506SBarry.Harding@Sun.COM 		}
201610506SBarry.Harding@Sun.COM 	}
201710506SBarry.Harding@Sun.COM }
201810506SBarry.Harding@Sun.COM 
201910506SBarry.Harding@Sun.COM static int
yge_intr_gmac(yge_port_t * port)202010506SBarry.Harding@Sun.COM yge_intr_gmac(yge_port_t *port)
202110506SBarry.Harding@Sun.COM {
202210506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
202310506SBarry.Harding@Sun.COM 	int pnum = port->p_port;
202410506SBarry.Harding@Sun.COM 	uint8_t status;
202510506SBarry.Harding@Sun.COM 	int dispatch_wrk = 0;
202610506SBarry.Harding@Sun.COM 
202710506SBarry.Harding@Sun.COM 	status = CSR_READ_1(dev, MR_ADDR(pnum, GMAC_IRQ_SRC));
202810506SBarry.Harding@Sun.COM 
202910506SBarry.Harding@Sun.COM 	/* GMAC Rx FIFO overrun. */
203010506SBarry.Harding@Sun.COM 	if ((status & GM_IS_RX_FF_OR) != 0) {
203110506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
203210506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Rx FIFO overrun!");
203310506SBarry.Harding@Sun.COM 		dispatch_wrk |= YGE_TASK_RESTART;
203410506SBarry.Harding@Sun.COM 	}
203510506SBarry.Harding@Sun.COM 	/* GMAC Tx FIFO underrun. */
203610506SBarry.Harding@Sun.COM 	if ((status & GM_IS_TX_FF_UR) != 0) {
203710506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
203810506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Tx FIFO underrun!");
203910506SBarry.Harding@Sun.COM 		/*
204010506SBarry.Harding@Sun.COM 		 * In case of Tx underrun, we may need to flush/reset
204110506SBarry.Harding@Sun.COM 		 * Tx MAC but that would also require
204210506SBarry.Harding@Sun.COM 		 * resynchronization with status LEs. Reinitializing
204310506SBarry.Harding@Sun.COM 		 * status LEs would affect the other port in dual MAC
204410506SBarry.Harding@Sun.COM 		 * configuration so it should be avoided if we can.
204510506SBarry.Harding@Sun.COM 		 * Due to lack of documentation it's all vague guess
204610506SBarry.Harding@Sun.COM 		 * but it needs more investigation.
204710506SBarry.Harding@Sun.COM 		 */
204810506SBarry.Harding@Sun.COM 	}
204910506SBarry.Harding@Sun.COM 	return (dispatch_wrk);
205010506SBarry.Harding@Sun.COM }
205110506SBarry.Harding@Sun.COM 
205210506SBarry.Harding@Sun.COM static void
yge_handle_hwerr(yge_port_t * port,uint32_t status)205310506SBarry.Harding@Sun.COM yge_handle_hwerr(yge_port_t *port, uint32_t status)
205410506SBarry.Harding@Sun.COM {
205510506SBarry.Harding@Sun.COM 	yge_dev_t	*dev = port->p_dev;
205610506SBarry.Harding@Sun.COM 
205710506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PAR_RD1) != 0) {
205810506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "RAM buffer read parity error");
205910506SBarry.Harding@Sun.COM 		/* Clear IRQ. */
206010506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, SELECT_RAM_BUFFER(port->p_port, B3_RI_CTRL),
206110506SBarry.Harding@Sun.COM 		    RI_CLR_RD_PERR);
206210506SBarry.Harding@Sun.COM 	}
206310506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PAR_WR1) != 0) {
206410506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "RAM buffer write parity error");
206510506SBarry.Harding@Sun.COM 		/* Clear IRQ. */
206610506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, SELECT_RAM_BUFFER(port->p_port, B3_RI_CTRL),
206710506SBarry.Harding@Sun.COM 		    RI_CLR_WR_PERR);
206810506SBarry.Harding@Sun.COM 	}
206910506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PAR_MAC1) != 0) {
207010506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Tx MAC parity error");
207110506SBarry.Harding@Sun.COM 		/* Clear IRQ. */
207210506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(port->p_port, TX_GMF_CTRL_T),
207310506SBarry.Harding@Sun.COM 		    GMF_CLI_TX_PE);
207410506SBarry.Harding@Sun.COM 	}
207510506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PAR_RX1) != 0) {
207610506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Rx parity error");
207710506SBarry.Harding@Sun.COM 		/* Clear IRQ. */
207810506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, Q_ADDR(port->p_rxq, Q_CSR), BMU_CLR_IRQ_PAR);
207910506SBarry.Harding@Sun.COM 	}
208010506SBarry.Harding@Sun.COM 	if ((status & (Y2_IS_TCP_TXS1 | Y2_IS_TCP_TXA1)) != 0) {
208110506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "TCP segmentation error");
208210506SBarry.Harding@Sun.COM 		/* Clear IRQ. */
208310506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, Q_ADDR(port->p_txq, Q_CSR), BMU_CLR_IRQ_TCP);
208410506SBarry.Harding@Sun.COM 	}
208510506SBarry.Harding@Sun.COM }
208610506SBarry.Harding@Sun.COM 
208710506SBarry.Harding@Sun.COM static void
yge_intr_hwerr(yge_dev_t * dev)208810506SBarry.Harding@Sun.COM yge_intr_hwerr(yge_dev_t *dev)
208910506SBarry.Harding@Sun.COM {
209010506SBarry.Harding@Sun.COM 	uint32_t status;
209110506SBarry.Harding@Sun.COM 	uint32_t tlphead[4];
209210506SBarry.Harding@Sun.COM 
209310506SBarry.Harding@Sun.COM 	status = CSR_READ_4(dev, B0_HWE_ISRC);
209410506SBarry.Harding@Sun.COM 	/* Time Stamp timer overflow. */
209510506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_TIST_OV) != 0)
209610506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
209710506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PCI_NEXP) != 0) {
209810506SBarry.Harding@Sun.COM 		/*
209910506SBarry.Harding@Sun.COM 		 * PCI Express Error occurred which is not described in PEX
210010506SBarry.Harding@Sun.COM 		 * spec.
210110506SBarry.Harding@Sun.COM 		 * This error is also mapped either to Master Abort(
210210506SBarry.Harding@Sun.COM 		 * Y2_IS_MST_ERR) or Target Abort (Y2_IS_IRQ_STAT) bit and
210310506SBarry.Harding@Sun.COM 		 * can only be cleared there.
210410506SBarry.Harding@Sun.COM 		 */
210510506SBarry.Harding@Sun.COM 		yge_error(dev, NULL, "PCI Express protocol violation error");
210610506SBarry.Harding@Sun.COM 	}
210710506SBarry.Harding@Sun.COM 
210810506SBarry.Harding@Sun.COM 	if ((status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) != 0) {
210910506SBarry.Harding@Sun.COM 		uint16_t v16;
211010506SBarry.Harding@Sun.COM 
211110506SBarry.Harding@Sun.COM 		if ((status & Y2_IS_IRQ_STAT) != 0)
211210506SBarry.Harding@Sun.COM 			yge_error(dev, NULL, "Unexpected IRQ Status error");
211310506SBarry.Harding@Sun.COM 		if ((status & Y2_IS_MST_ERR) != 0)
211410506SBarry.Harding@Sun.COM 			yge_error(dev, NULL, "Unexpected IRQ Master error");
211510506SBarry.Harding@Sun.COM 		/* Reset all bits in the PCI status register. */
211610506SBarry.Harding@Sun.COM 		v16 = pci_config_get16(dev->d_pcih, PCI_CONF_STAT);
211710506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
211810506SBarry.Harding@Sun.COM 		pci_config_put16(dev->d_pcih, PCI_CONF_STAT, v16 |
211910506SBarry.Harding@Sun.COM 		    PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB |
212010506SBarry.Harding@Sun.COM 		    PCI_STAT_R_TARG_AB | PCI_STAT_PERROR);
212110506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
212210506SBarry.Harding@Sun.COM 	}
212310506SBarry.Harding@Sun.COM 
212410506SBarry.Harding@Sun.COM 	/* Check for PCI Express Uncorrectable Error. */
212510506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_PCI_EXP) != 0) {
212610506SBarry.Harding@Sun.COM 		uint32_t v32;
212710506SBarry.Harding@Sun.COM 
212810506SBarry.Harding@Sun.COM 		/*
212910506SBarry.Harding@Sun.COM 		 * On PCI Express bus bridges are called root complexes (RC).
213010506SBarry.Harding@Sun.COM 		 * PCI Express errors are recognized by the root complex too,
213110506SBarry.Harding@Sun.COM 		 * which requests the system to handle the problem. After
213210506SBarry.Harding@Sun.COM 		 * error occurrence it may be that no access to the adapter
213310506SBarry.Harding@Sun.COM 		 * may be performed any longer.
213410506SBarry.Harding@Sun.COM 		 */
213510506SBarry.Harding@Sun.COM 
213610506SBarry.Harding@Sun.COM 		v32 = CSR_PCI_READ_4(dev, PEX_UNC_ERR_STAT);
213710506SBarry.Harding@Sun.COM 		if ((v32 & PEX_UNSUP_REQ) != 0) {
213810506SBarry.Harding@Sun.COM 			/* Ignore unsupported request error. */
213910506SBarry.Harding@Sun.COM 			yge_error(dev, NULL,
214010506SBarry.Harding@Sun.COM 			    "Uncorrectable PCI Express error");
214110506SBarry.Harding@Sun.COM 		}
214210506SBarry.Harding@Sun.COM 		if ((v32 & (PEX_FATAL_ERRORS | PEX_POIS_TLP)) != 0) {
214310506SBarry.Harding@Sun.COM 			int i;
214410506SBarry.Harding@Sun.COM 
214510506SBarry.Harding@Sun.COM 			/* Get TLP header form Log Registers. */
214610506SBarry.Harding@Sun.COM 			for (i = 0; i < 4; i++)
214710506SBarry.Harding@Sun.COM 				tlphead[i] = CSR_PCI_READ_4(dev,
214810506SBarry.Harding@Sun.COM 				    PEX_HEADER_LOG + i * 4);
214910506SBarry.Harding@Sun.COM 			/* Check for vendor defined broadcast message. */
215010506SBarry.Harding@Sun.COM 			if (!(tlphead[0] == 0x73004001 && tlphead[1] == 0x7f)) {
215110506SBarry.Harding@Sun.COM 				dev->d_intrhwemask &= ~Y2_IS_PCI_EXP;
215210506SBarry.Harding@Sun.COM 				CSR_WRITE_4(dev, B0_HWE_IMSK,
215310506SBarry.Harding@Sun.COM 				    dev->d_intrhwemask);
215410506SBarry.Harding@Sun.COM 				(void) CSR_READ_4(dev, B0_HWE_IMSK);
215510506SBarry.Harding@Sun.COM 			}
215610506SBarry.Harding@Sun.COM 		}
215710506SBarry.Harding@Sun.COM 		/* Clear the interrupt. */
215810506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
215910506SBarry.Harding@Sun.COM 		CSR_PCI_WRITE_4(dev, PEX_UNC_ERR_STAT, 0xffffffff);
216010506SBarry.Harding@Sun.COM 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
216110506SBarry.Harding@Sun.COM 	}
216210506SBarry.Harding@Sun.COM 
216310506SBarry.Harding@Sun.COM 	if ((status & Y2_HWE_L1_MASK) != 0 && dev->d_port[YGE_PORT_A] != NULL)
216410506SBarry.Harding@Sun.COM 		yge_handle_hwerr(dev->d_port[YGE_PORT_A], status);
216510506SBarry.Harding@Sun.COM 	if ((status & Y2_HWE_L2_MASK) != 0 && dev->d_port[YGE_PORT_B] != NULL)
216610506SBarry.Harding@Sun.COM 		yge_handle_hwerr(dev->d_port[YGE_PORT_B], status >> 8);
216710506SBarry.Harding@Sun.COM }
216810506SBarry.Harding@Sun.COM 
216910506SBarry.Harding@Sun.COM /*
217010506SBarry.Harding@Sun.COM  * Returns B_TRUE if there is potentially more work to do.
217110506SBarry.Harding@Sun.COM  */
217210506SBarry.Harding@Sun.COM static boolean_t
yge_handle_events(yge_dev_t * dev,mblk_t ** heads,mblk_t ** tails,int * txindex)217310506SBarry.Harding@Sun.COM yge_handle_events(yge_dev_t *dev, mblk_t **heads, mblk_t **tails, int *txindex)
217410506SBarry.Harding@Sun.COM {
217510506SBarry.Harding@Sun.COM 	yge_port_t *port;
217610506SBarry.Harding@Sun.COM 	yge_ring_t *ring;
217710506SBarry.Harding@Sun.COM 	uint32_t control, status;
217810506SBarry.Harding@Sun.COM 	int cons, idx, len, pnum;
217910506SBarry.Harding@Sun.COM 	mblk_t *mp;
218010506SBarry.Harding@Sun.COM 	uint32_t rxprogs[2];
218110506SBarry.Harding@Sun.COM 
218210506SBarry.Harding@Sun.COM 	rxprogs[0] = rxprogs[1] = 0;
218310506SBarry.Harding@Sun.COM 
218410506SBarry.Harding@Sun.COM 	idx = CSR_READ_2(dev, STAT_PUT_IDX);
218510506SBarry.Harding@Sun.COM 	if (idx == dev->d_stat_cons) {
218610506SBarry.Harding@Sun.COM 		return (B_FALSE);
218710506SBarry.Harding@Sun.COM 	}
218810506SBarry.Harding@Sun.COM 
218910506SBarry.Harding@Sun.COM 	ring = &dev->d_status_ring;
219010506SBarry.Harding@Sun.COM 
219110506SBarry.Harding@Sun.COM 	for (cons = dev->d_stat_cons; cons != idx; ) {
219210506SBarry.Harding@Sun.COM 		/* Sync status LE. */
219310506SBarry.Harding@Sun.COM 		SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORKERNEL);
219410506SBarry.Harding@Sun.COM 		control = GETCTRL(ring, cons);
219510506SBarry.Harding@Sun.COM 		if ((control & HW_OWNER) == 0) {
219610506SBarry.Harding@Sun.COM 			yge_error(dev, NULL, "Status descriptor error: "
219710506SBarry.Harding@Sun.COM 			    "index %d, control %x", cons, control);
219810506SBarry.Harding@Sun.COM 			break;
219910506SBarry.Harding@Sun.COM 		}
220010506SBarry.Harding@Sun.COM 
220110506SBarry.Harding@Sun.COM 		status = GETSTAT(ring, cons);
220210506SBarry.Harding@Sun.COM 
220310506SBarry.Harding@Sun.COM 		control &= ~HW_OWNER;
220410506SBarry.Harding@Sun.COM 		len = control & STLE_LEN_MASK;
220510506SBarry.Harding@Sun.COM 		pnum = ((control >> 16) & 0x01);
220610506SBarry.Harding@Sun.COM 		port = dev->d_port[pnum];
220710506SBarry.Harding@Sun.COM 		if (port == NULL) {
220810506SBarry.Harding@Sun.COM 			yge_error(dev, NULL, "Invalid port opcode: 0x%08x",
220910506SBarry.Harding@Sun.COM 			    control & STLE_OP_MASK);
221010506SBarry.Harding@Sun.COM 			goto finish;
221110506SBarry.Harding@Sun.COM 		}
221210506SBarry.Harding@Sun.COM 
221310506SBarry.Harding@Sun.COM 		switch (control & STLE_OP_MASK) {
221410506SBarry.Harding@Sun.COM 		case OP_RXSTAT:
221510506SBarry.Harding@Sun.COM 			mp = yge_rxeof(port, status, len);
221610506SBarry.Harding@Sun.COM 			if (mp != NULL) {
221710506SBarry.Harding@Sun.COM 				if (heads[pnum] == NULL)
221810506SBarry.Harding@Sun.COM 					heads[pnum] = mp;
221910506SBarry.Harding@Sun.COM 				else
222010506SBarry.Harding@Sun.COM 					tails[pnum]->b_next = mp;
222110506SBarry.Harding@Sun.COM 				tails[pnum] = mp;
222210506SBarry.Harding@Sun.COM 			}
222310506SBarry.Harding@Sun.COM 
222410506SBarry.Harding@Sun.COM 			rxprogs[pnum]++;
222510506SBarry.Harding@Sun.COM 			break;
222610506SBarry.Harding@Sun.COM 
222710506SBarry.Harding@Sun.COM 		case OP_TXINDEXLE:
222810506SBarry.Harding@Sun.COM 			txindex[0] = status & STLE_TXA1_MSKL;
222910506SBarry.Harding@Sun.COM 			txindex[1] =
223010506SBarry.Harding@Sun.COM 			    ((status & STLE_TXA2_MSKL) >> STLE_TXA2_SHIFTL) |
223110506SBarry.Harding@Sun.COM 			    ((len & STLE_TXA2_MSKH) << STLE_TXA2_SHIFTH);
223210506SBarry.Harding@Sun.COM 			break;
223310506SBarry.Harding@Sun.COM 		default:
223410506SBarry.Harding@Sun.COM 			yge_error(dev, NULL, "Unhandled opcode: 0x%08x",
223510506SBarry.Harding@Sun.COM 			    control & STLE_OP_MASK);
223610506SBarry.Harding@Sun.COM 			break;
223710506SBarry.Harding@Sun.COM 		}
223810506SBarry.Harding@Sun.COM finish:
223910506SBarry.Harding@Sun.COM 
224010506SBarry.Harding@Sun.COM 		/* Give it back to HW. */
224110506SBarry.Harding@Sun.COM 		PUTCTRL(ring, cons, control);
224210506SBarry.Harding@Sun.COM 		SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORDEV);
224310506SBarry.Harding@Sun.COM 
224410506SBarry.Harding@Sun.COM 		YGE_INC(cons, YGE_STAT_RING_CNT);
224510506SBarry.Harding@Sun.COM 		if (rxprogs[pnum] > dev->d_process_limit) {
224610506SBarry.Harding@Sun.COM 			break;
224710506SBarry.Harding@Sun.COM 		}
224810506SBarry.Harding@Sun.COM 	}
224910506SBarry.Harding@Sun.COM 
225010506SBarry.Harding@Sun.COM 	dev->d_stat_cons = cons;
225110506SBarry.Harding@Sun.COM 	if (dev->d_stat_cons != CSR_READ_2(dev, STAT_PUT_IDX))
225210506SBarry.Harding@Sun.COM 		return (B_TRUE);
225310506SBarry.Harding@Sun.COM 	else
225410506SBarry.Harding@Sun.COM 		return (B_FALSE);
225510506SBarry.Harding@Sun.COM }
225610506SBarry.Harding@Sun.COM 
225710506SBarry.Harding@Sun.COM /*ARGSUSED1*/
225810506SBarry.Harding@Sun.COM static uint_t
yge_intr(caddr_t arg1,caddr_t arg2)225910506SBarry.Harding@Sun.COM yge_intr(caddr_t arg1, caddr_t arg2)
226010506SBarry.Harding@Sun.COM {
226110506SBarry.Harding@Sun.COM 	yge_dev_t	*dev;
226210506SBarry.Harding@Sun.COM 	yge_port_t	*port1;
226310506SBarry.Harding@Sun.COM 	yge_port_t	*port2;
226410506SBarry.Harding@Sun.COM 	uint32_t	status;
226510506SBarry.Harding@Sun.COM 	mblk_t		*heads[2], *tails[2];
226610506SBarry.Harding@Sun.COM 	int		txindex[2];
226710506SBarry.Harding@Sun.COM 	int		dispatch_wrk;
226810506SBarry.Harding@Sun.COM 
226910506SBarry.Harding@Sun.COM 	dev = (void *)arg1;
227010506SBarry.Harding@Sun.COM 
227110506SBarry.Harding@Sun.COM 	heads[0] = heads[1] = NULL;
227210506SBarry.Harding@Sun.COM 	tails[0] = tails[1] = NULL;
227310506SBarry.Harding@Sun.COM 	txindex[0] = txindex[1] = -1;
227410506SBarry.Harding@Sun.COM 	dispatch_wrk = 0;
227510506SBarry.Harding@Sun.COM 
227610506SBarry.Harding@Sun.COM 	port1 = dev->d_port[YGE_PORT_A];
227710506SBarry.Harding@Sun.COM 	port2 = dev->d_port[YGE_PORT_B];
227810506SBarry.Harding@Sun.COM 
227910506SBarry.Harding@Sun.COM 	RX_LOCK(dev);
228010506SBarry.Harding@Sun.COM 
228110506SBarry.Harding@Sun.COM 	if (dev->d_suspended) {
228210506SBarry.Harding@Sun.COM 		RX_UNLOCK(dev);
228310506SBarry.Harding@Sun.COM 		return (DDI_INTR_UNCLAIMED);
228410506SBarry.Harding@Sun.COM 	}
228510506SBarry.Harding@Sun.COM 
228610506SBarry.Harding@Sun.COM 	/* Get interrupt source. */
228710506SBarry.Harding@Sun.COM 	status = CSR_READ_4(dev, B0_Y2_SP_ISRC2);
228810506SBarry.Harding@Sun.COM 	if (status == 0 || status == 0xffffffff ||
228910506SBarry.Harding@Sun.COM 	    (status & dev->d_intrmask) == 0) { /* Stray interrupt ? */
229010506SBarry.Harding@Sun.COM 		/* Reenable interrupts. */
229110506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, B0_Y2_SP_ICR, 2);
229210506SBarry.Harding@Sun.COM 		RX_UNLOCK(dev);
229310506SBarry.Harding@Sun.COM 		return (DDI_INTR_UNCLAIMED);
229410506SBarry.Harding@Sun.COM 	}
229510506SBarry.Harding@Sun.COM 
229610506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_HW_ERR) != 0) {
229710506SBarry.Harding@Sun.COM 		yge_intr_hwerr(dev);
229810506SBarry.Harding@Sun.COM 	}
229910506SBarry.Harding@Sun.COM 
230010506SBarry.Harding@Sun.COM 	if (status & Y2_IS_IRQ_MAC1) {
230110506SBarry.Harding@Sun.COM 		dispatch_wrk |= yge_intr_gmac(port1);
230210506SBarry.Harding@Sun.COM 	}
230310506SBarry.Harding@Sun.COM 	if (status & Y2_IS_IRQ_MAC2) {
230410506SBarry.Harding@Sun.COM 		dispatch_wrk |= yge_intr_gmac(port2);
230510506SBarry.Harding@Sun.COM 	}
230610506SBarry.Harding@Sun.COM 
230710506SBarry.Harding@Sun.COM 	if ((status & (Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2)) != 0) {
230810506SBarry.Harding@Sun.COM 		yge_error(NULL, status & Y2_IS_CHK_RX1 ? port1 : port2,
230910506SBarry.Harding@Sun.COM 		    "Rx descriptor error");
231010506SBarry.Harding@Sun.COM 		dev->d_intrmask &= ~(Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2);
231110506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
231210506SBarry.Harding@Sun.COM 		(void) CSR_READ_4(dev, B0_IMSK);
231310506SBarry.Harding@Sun.COM 	}
231410506SBarry.Harding@Sun.COM 	if ((status & (Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2)) != 0) {
231510506SBarry.Harding@Sun.COM 		yge_error(NULL, status & Y2_IS_CHK_TXA1 ? port1 : port2,
231610506SBarry.Harding@Sun.COM 		    "Tx descriptor error");
231710506SBarry.Harding@Sun.COM 		dev->d_intrmask &= ~(Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2);
231810506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
231910506SBarry.Harding@Sun.COM 		(void) CSR_READ_4(dev, B0_IMSK);
232010506SBarry.Harding@Sun.COM 	}
232110506SBarry.Harding@Sun.COM 
232210506SBarry.Harding@Sun.COM 	/* handle events until it returns false */
232310506SBarry.Harding@Sun.COM 	while (yge_handle_events(dev, heads, tails, txindex))
232410506SBarry.Harding@Sun.COM 		/* NOP */;
232510506SBarry.Harding@Sun.COM 
232610506SBarry.Harding@Sun.COM 	/* Do receive/transmit events */
232710506SBarry.Harding@Sun.COM 	if ((status & Y2_IS_STAT_BMU)) {
232810506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_CLR_IRQ);
232910506SBarry.Harding@Sun.COM 	}
233010506SBarry.Harding@Sun.COM 
233110506SBarry.Harding@Sun.COM 	/* Reenable interrupts. */
233210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_Y2_SP_ICR, 2);
233310506SBarry.Harding@Sun.COM 
233410506SBarry.Harding@Sun.COM 	RX_UNLOCK(dev);
233510506SBarry.Harding@Sun.COM 
233610506SBarry.Harding@Sun.COM 	if (dispatch_wrk) {
233710506SBarry.Harding@Sun.COM 		yge_dispatch(dev, dispatch_wrk);
233810506SBarry.Harding@Sun.COM 	}
233910506SBarry.Harding@Sun.COM 
234010506SBarry.Harding@Sun.COM 	if (port1->p_running) {
234110506SBarry.Harding@Sun.COM 		if (txindex[0] >= 0) {
234210506SBarry.Harding@Sun.COM 			yge_txeof(port1, txindex[0]);
234310506SBarry.Harding@Sun.COM 		}
234410506SBarry.Harding@Sun.COM 		if (heads[0])
234510506SBarry.Harding@Sun.COM 			mac_rx(port1->p_mh, NULL, heads[0]);
234610506SBarry.Harding@Sun.COM 	} else {
234710506SBarry.Harding@Sun.COM 		if (heads[0]) {
234810506SBarry.Harding@Sun.COM 			mblk_t *mp;
234910506SBarry.Harding@Sun.COM 			while ((mp = heads[0]) != NULL) {
235010506SBarry.Harding@Sun.COM 				heads[0] = mp->b_next;
235110506SBarry.Harding@Sun.COM 				freemsg(mp);
235210506SBarry.Harding@Sun.COM 			}
235310506SBarry.Harding@Sun.COM 		}
235410506SBarry.Harding@Sun.COM 	}
235510506SBarry.Harding@Sun.COM 
235610506SBarry.Harding@Sun.COM 	if (port2->p_running) {
235710506SBarry.Harding@Sun.COM 		if (txindex[1] >= 0) {
235810506SBarry.Harding@Sun.COM 			yge_txeof(port2, txindex[1]);
235910506SBarry.Harding@Sun.COM 		}
236010506SBarry.Harding@Sun.COM 		if (heads[1])
236110506SBarry.Harding@Sun.COM 			mac_rx(port2->p_mh, NULL, heads[1]);
236210506SBarry.Harding@Sun.COM 	} else {
236310506SBarry.Harding@Sun.COM 		if (heads[1]) {
236410506SBarry.Harding@Sun.COM 			mblk_t *mp;
236510506SBarry.Harding@Sun.COM 			while ((mp = heads[1]) != NULL) {
236610506SBarry.Harding@Sun.COM 				heads[1] = mp->b_next;
236710506SBarry.Harding@Sun.COM 				freemsg(mp);
236810506SBarry.Harding@Sun.COM 			}
236910506SBarry.Harding@Sun.COM 		}
237010506SBarry.Harding@Sun.COM 	}
237110506SBarry.Harding@Sun.COM 
237210506SBarry.Harding@Sun.COM 	return (DDI_INTR_CLAIMED);
237310506SBarry.Harding@Sun.COM }
237410506SBarry.Harding@Sun.COM 
237510506SBarry.Harding@Sun.COM static void
yge_set_tx_stfwd(yge_port_t * port)237610506SBarry.Harding@Sun.COM yge_set_tx_stfwd(yge_port_t *port)
237710506SBarry.Harding@Sun.COM {
237810506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
237910506SBarry.Harding@Sun.COM 	int pnum = port->p_port;
238010506SBarry.Harding@Sun.COM 
238110506SBarry.Harding@Sun.COM 	switch (dev->d_hw_id) {
238210506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EX:
238310506SBarry.Harding@Sun.COM 		if (dev->d_hw_rev == CHIP_REV_YU_EX_A0)
238410506SBarry.Harding@Sun.COM 			goto yukon_ex_workaround;
238510506SBarry.Harding@Sun.COM 
238610506SBarry.Harding@Sun.COM 		if (port->p_mtu > ETHERMTU)
238710506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
238810506SBarry.Harding@Sun.COM 			    TX_JUMBO_ENA | TX_STFW_ENA);
238910506SBarry.Harding@Sun.COM 		else
239010506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
239110506SBarry.Harding@Sun.COM 			    TX_JUMBO_DIS | TX_STFW_ENA);
239210506SBarry.Harding@Sun.COM 		break;
239310506SBarry.Harding@Sun.COM 	default:
239410506SBarry.Harding@Sun.COM yukon_ex_workaround:
239510506SBarry.Harding@Sun.COM 		if (port->p_mtu > ETHERMTU) {
239610506SBarry.Harding@Sun.COM 			/* Set Tx GMAC FIFO Almost Empty Threshold. */
239710506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_AE_THR),
239810506SBarry.Harding@Sun.COM 			    MSK_ECU_JUMBO_WM << 16 | MSK_ECU_AE_THR);
239910506SBarry.Harding@Sun.COM 			/* Disable Store & Forward mode for Tx. */
240010506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
240110506SBarry.Harding@Sun.COM 			    TX_JUMBO_ENA | TX_STFW_DIS);
240210506SBarry.Harding@Sun.COM 		} else {
240310506SBarry.Harding@Sun.COM 			/* Enable Store & Forward mode for Tx. */
240410506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
240510506SBarry.Harding@Sun.COM 			    TX_JUMBO_DIS | TX_STFW_ENA);
240610506SBarry.Harding@Sun.COM 		}
240710506SBarry.Harding@Sun.COM 		break;
240810506SBarry.Harding@Sun.COM 	}
240910506SBarry.Harding@Sun.COM }
241010506SBarry.Harding@Sun.COM 
241110506SBarry.Harding@Sun.COM static void
yge_start_port(yge_port_t * port)241210506SBarry.Harding@Sun.COM yge_start_port(yge_port_t *port)
241310506SBarry.Harding@Sun.COM {
241410506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
241510506SBarry.Harding@Sun.COM 	uint16_t gmac;
241610506SBarry.Harding@Sun.COM 	int32_t pnum;
241710506SBarry.Harding@Sun.COM 	int32_t rxq;
241810506SBarry.Harding@Sun.COM 	int32_t txq;
241910506SBarry.Harding@Sun.COM 	uint32_t reg;
242010506SBarry.Harding@Sun.COM 
242110506SBarry.Harding@Sun.COM 	pnum = port->p_port;
242210506SBarry.Harding@Sun.COM 	txq = port->p_txq;
242310506SBarry.Harding@Sun.COM 	rxq = port->p_rxq;
242410506SBarry.Harding@Sun.COM 
242510506SBarry.Harding@Sun.COM 	if (port->p_mtu < ETHERMTU)
242610506SBarry.Harding@Sun.COM 		port->p_framesize = ETHERMTU;
242710506SBarry.Harding@Sun.COM 	else
242810506SBarry.Harding@Sun.COM 		port->p_framesize = port->p_mtu;
242910506SBarry.Harding@Sun.COM 	port->p_framesize += sizeof (struct ether_vlan_header);
243010506SBarry.Harding@Sun.COM 
243110506SBarry.Harding@Sun.COM 	/*
243210506SBarry.Harding@Sun.COM 	 * Note for the future, if we enable offloads:
243310506SBarry.Harding@Sun.COM 	 * In Yukon EC Ultra, TSO & checksum offload is not
243410506SBarry.Harding@Sun.COM 	 * supported for jumbo frame.
243510506SBarry.Harding@Sun.COM 	 */
243610506SBarry.Harding@Sun.COM 
243710506SBarry.Harding@Sun.COM 	/* GMAC Control reset */
243810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_RST_SET);
243910506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_RST_CLR);
244010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_F_LOOPB_OFF);
244110506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_EX)
244210506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL),
244310506SBarry.Harding@Sun.COM 		    GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON |
244410506SBarry.Harding@Sun.COM 		    GMC_BYP_RETR_ON);
244510506SBarry.Harding@Sun.COM 	/*
244610506SBarry.Harding@Sun.COM 	 * Initialize GMAC first such that speed/duplex/flow-control
244710506SBarry.Harding@Sun.COM 	 * parameters are renegotiated with the interface is brought up.
244810506SBarry.Harding@Sun.COM 	 */
244910506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_GP_CTRL, 0);
245010506SBarry.Harding@Sun.COM 
245110506SBarry.Harding@Sun.COM 	/* Dummy read the Interrupt Source Register. */
245210506SBarry.Harding@Sun.COM 	(void) CSR_READ_1(dev, MR_ADDR(pnum, GMAC_IRQ_SRC));
245310506SBarry.Harding@Sun.COM 
245410506SBarry.Harding@Sun.COM 	/* Clear MIB stats. */
245510506SBarry.Harding@Sun.COM 	yge_stats_clear(port);
245610506SBarry.Harding@Sun.COM 
245710506SBarry.Harding@Sun.COM 	/* Disable FCS. */
245810506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_RX_CTRL, GM_RXCR_CRC_DIS);
245910506SBarry.Harding@Sun.COM 
246010506SBarry.Harding@Sun.COM 	/* Setup Transmit Control Register. */
246110506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
246210506SBarry.Harding@Sun.COM 
246310506SBarry.Harding@Sun.COM 	/* Setup Transmit Flow Control Register. */
246410506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_TX_FLOW_CTRL, 0xffff);
246510506SBarry.Harding@Sun.COM 
246610506SBarry.Harding@Sun.COM 	/* Setup Transmit Parameter Register. */
246710506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_TX_PARAM,
246810506SBarry.Harding@Sun.COM 	    TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) | TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
246910506SBarry.Harding@Sun.COM 	    TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) | TX_BACK_OFF_LIM(TX_BOF_LIM_DEF));
247010506SBarry.Harding@Sun.COM 
247110506SBarry.Harding@Sun.COM 	gmac = DATA_BLIND_VAL(DATA_BLIND_DEF) |
247210506SBarry.Harding@Sun.COM 	    GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
247310506SBarry.Harding@Sun.COM 
247410506SBarry.Harding@Sun.COM 	if (port->p_mtu > ETHERMTU)
247510506SBarry.Harding@Sun.COM 		gmac |= GM_SMOD_JUMBO_ENA;
247610506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_SERIAL_MODE, gmac);
247710506SBarry.Harding@Sun.COM 
247810506SBarry.Harding@Sun.COM 	/* Disable interrupts for counter overflows. */
247910506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_TX_IRQ_MSK, 0);
248010506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_RX_IRQ_MSK, 0);
248110506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_TR_IRQ_MSK, 0);
248210506SBarry.Harding@Sun.COM 
248310506SBarry.Harding@Sun.COM 	/* Configure Rx MAC FIFO. */
248410506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_SET);
248510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_CLR);
248610506SBarry.Harding@Sun.COM 	reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
248710506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_FE_P ||
248810506SBarry.Harding@Sun.COM 	    dev->d_hw_id == CHIP_ID_YUKON_EX)
248910506SBarry.Harding@Sun.COM 		reg |= GMF_RX_OVER_ON;
249010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), reg);
249110506SBarry.Harding@Sun.COM 
249210506SBarry.Harding@Sun.COM 	/* Set receive filter. */
249310506SBarry.Harding@Sun.COM 	yge_setrxfilt(port);
249410506SBarry.Harding@Sun.COM 
249510506SBarry.Harding@Sun.COM 	/* Flush Rx MAC FIFO on any flow control or error. */
249610506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_FL_MSK), GMR_FS_ANY_ERR);
249710506SBarry.Harding@Sun.COM 
249810506SBarry.Harding@Sun.COM 	/*
249910506SBarry.Harding@Sun.COM 	 * Set Rx FIFO flush threshold to 64 bytes + 1 FIFO word
250010506SBarry.Harding@Sun.COM 	 * due to hardware hang on receipt of pause frames.
250110506SBarry.Harding@Sun.COM 	 */
250210506SBarry.Harding@Sun.COM 	reg = RX_GMF_FL_THR_DEF + 1;
250310506SBarry.Harding@Sun.COM 	/* FE+ magic */
250410506SBarry.Harding@Sun.COM 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
250510506SBarry.Harding@Sun.COM 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0))
250610506SBarry.Harding@Sun.COM 		reg = 0x178;
250710506SBarry.Harding@Sun.COM 
250810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_FL_THR), reg);
250910506SBarry.Harding@Sun.COM 
251010506SBarry.Harding@Sun.COM 	/* Configure Tx MAC FIFO. */
251110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_SET);
251210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_CLR);
251310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_OPER_ON);
251410506SBarry.Harding@Sun.COM 
251510506SBarry.Harding@Sun.COM 	/* Disable hardware VLAN tag insertion/stripping. */
251610506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
251710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
251810506SBarry.Harding@Sun.COM 
251910506SBarry.Harding@Sun.COM 	if ((port->p_flags & PORT_FLAG_RAMBUF) == 0) {
252010506SBarry.Harding@Sun.COM 		/* Set Rx Pause threshold. */
252110506SBarry.Harding@Sun.COM 		if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
252210506SBarry.Harding@Sun.COM 		    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
252310506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_LP_THR),
252410506SBarry.Harding@Sun.COM 			    MSK_ECU_LLPP);
252510506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_UP_THR),
252610506SBarry.Harding@Sun.COM 			    MSK_FEP_ULPP);
252710506SBarry.Harding@Sun.COM 		} else {
252810506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_LP_THR),
252910506SBarry.Harding@Sun.COM 			    MSK_ECU_LLPP);
253010506SBarry.Harding@Sun.COM 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_UP_THR),
253110506SBarry.Harding@Sun.COM 			    MSK_ECU_ULPP);
253210506SBarry.Harding@Sun.COM 		}
253310506SBarry.Harding@Sun.COM 		/* Configure store-and-forward for TX */
253410506SBarry.Harding@Sun.COM 		yge_set_tx_stfwd(port);
253510506SBarry.Harding@Sun.COM 	}
253610506SBarry.Harding@Sun.COM 
253710506SBarry.Harding@Sun.COM 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
253810506SBarry.Harding@Sun.COM 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
253910506SBarry.Harding@Sun.COM 		/* Disable dynamic watermark */
254010506SBarry.Harding@Sun.COM 		reg = CSR_READ_4(dev, MR_ADDR(pnum, TX_GMF_EA));
254110506SBarry.Harding@Sun.COM 		reg &= ~TX_DYN_WM_ENA;
254210506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_EA), reg);
254310506SBarry.Harding@Sun.COM 	}
254410506SBarry.Harding@Sun.COM 
254510506SBarry.Harding@Sun.COM 	/*
254610506SBarry.Harding@Sun.COM 	 * Disable Force Sync bit and Alloc bit in Tx RAM interface
254710506SBarry.Harding@Sun.COM 	 * arbiter as we don't use Sync Tx queue.
254810506SBarry.Harding@Sun.COM 	 */
254910506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL),
255010506SBarry.Harding@Sun.COM 	    TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
255110506SBarry.Harding@Sun.COM 	/* Enable the RAM Interface Arbiter. */
255210506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL), TXA_ENA_ARB);
255310506SBarry.Harding@Sun.COM 
255410506SBarry.Harding@Sun.COM 	/* Setup RAM buffer. */
255510506SBarry.Harding@Sun.COM 	yge_set_rambuffer(port);
255610506SBarry.Harding@Sun.COM 
255710506SBarry.Harding@Sun.COM 	/* Disable Tx sync Queue. */
255810506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(port->p_txsq, RB_CTRL), RB_RST_SET);
255910506SBarry.Harding@Sun.COM 
256010506SBarry.Harding@Sun.COM 	/* Setup Tx Queue Bus Memory Interface. */
256110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_CLR_RESET);
256210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_OPER_INIT);
256310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_FIFO_OP_ON);
256410506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, Q_ADDR(txq, Q_WM), MSK_BMU_TX_WM);
256510506SBarry.Harding@Sun.COM 
256610506SBarry.Harding@Sun.COM 	switch (dev->d_hw_id) {
256710506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EC_U:
256810506SBarry.Harding@Sun.COM 		if (dev->d_hw_rev == CHIP_REV_YU_EC_U_A0) {
256910506SBarry.Harding@Sun.COM 			/* Fix for Yukon-EC Ultra: set BMU FIFO level */
257010506SBarry.Harding@Sun.COM 			CSR_WRITE_2(dev, Q_ADDR(txq, Q_AL), MSK_ECU_TXFF_LEV);
257110506SBarry.Harding@Sun.COM 		}
257210506SBarry.Harding@Sun.COM 		break;
257310506SBarry.Harding@Sun.COM 	case CHIP_ID_YUKON_EX:
257410506SBarry.Harding@Sun.COM 		/*
257510506SBarry.Harding@Sun.COM 		 * Yukon Extreme seems to have silicon bug for
257610506SBarry.Harding@Sun.COM 		 * automatic Tx checksum calculation capability.
257710506SBarry.Harding@Sun.COM 		 */
257810506SBarry.Harding@Sun.COM 		if (dev->d_hw_rev == CHIP_REV_YU_EX_B0)
257910506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, Q_ADDR(txq, Q_F), F_TX_CHK_AUTO_OFF);
258010506SBarry.Harding@Sun.COM 		break;
258110506SBarry.Harding@Sun.COM 	}
258210506SBarry.Harding@Sun.COM 
258310506SBarry.Harding@Sun.COM 	/* Setup Rx Queue Bus Memory Interface. */
258410506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_CLR_RESET);
258510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_OPER_INIT);
258610506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_FIFO_OP_ON);
258710506SBarry.Harding@Sun.COM 	if (dev->d_bustype == PEX_BUS) {
258810506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, Q_ADDR(rxq, Q_WM), 0x80);
258910506SBarry.Harding@Sun.COM 	} else {
259010506SBarry.Harding@Sun.COM 		CSR_WRITE_2(dev, Q_ADDR(rxq, Q_WM), MSK_BMU_RX_WM);
259110506SBarry.Harding@Sun.COM 	}
259210506SBarry.Harding@Sun.COM 	if (dev->d_hw_id == CHIP_ID_YUKON_EC_U &&
259310506SBarry.Harding@Sun.COM 	    dev->d_hw_rev >= CHIP_REV_YU_EC_U_A1) {
259410506SBarry.Harding@Sun.COM 		/* MAC Rx RAM Read is controlled by hardware. */
259510506SBarry.Harding@Sun.COM 		CSR_WRITE_4(dev, Q_ADDR(rxq, Q_F), F_M_RX_RAM_DIS);
259610506SBarry.Harding@Sun.COM 	}
259710506SBarry.Harding@Sun.COM 
259810506SBarry.Harding@Sun.COM 	yge_init_tx_ring(port);
259910506SBarry.Harding@Sun.COM 
260010506SBarry.Harding@Sun.COM 	/* Disable Rx checksum offload and RSS hash. */
260110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR),
260210506SBarry.Harding@Sun.COM 	    BMU_DIS_RX_CHKSUM | BMU_DIS_RX_RSS_HASH);
260310506SBarry.Harding@Sun.COM 
260410506SBarry.Harding@Sun.COM 	yge_init_rx_ring(port);
260510506SBarry.Harding@Sun.COM 
260610506SBarry.Harding@Sun.COM 	/* Configure interrupt handling. */
260710506SBarry.Harding@Sun.COM 	if (port == dev->d_port[YGE_PORT_A]) {
260810506SBarry.Harding@Sun.COM 		dev->d_intrmask |= Y2_IS_PORT_A;
260910506SBarry.Harding@Sun.COM 		dev->d_intrhwemask |= Y2_HWE_L1_MASK;
261010506SBarry.Harding@Sun.COM 	} else if (port == dev->d_port[YGE_PORT_B]) {
261110506SBarry.Harding@Sun.COM 		dev->d_intrmask |= Y2_IS_PORT_B;
261210506SBarry.Harding@Sun.COM 		dev->d_intrhwemask |= Y2_HWE_L2_MASK;
261310506SBarry.Harding@Sun.COM 	}
261410506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, dev->d_intrhwemask);
261510506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
261610506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
261710506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
261810506SBarry.Harding@Sun.COM 
261910506SBarry.Harding@Sun.COM 	/* Enable RX/TX GMAC */
262010506SBarry.Harding@Sun.COM 	gmac = GMAC_READ_2(dev, pnum, GM_GP_CTRL);
262110506SBarry.Harding@Sun.COM 	gmac |= (GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
262210506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(port->p_dev, port->p_port, GM_GP_CTRL, gmac);
262310506SBarry.Harding@Sun.COM 	/* Read again to ensure writing. */
262410506SBarry.Harding@Sun.COM 	(void) GMAC_READ_2(dev, pnum, GM_GP_CTRL);
262510506SBarry.Harding@Sun.COM 
262610506SBarry.Harding@Sun.COM 	/* Reset TX timer */
262710506SBarry.Harding@Sun.COM 	port->p_tx_wdog = 0;
262810506SBarry.Harding@Sun.COM }
262910506SBarry.Harding@Sun.COM 
263010506SBarry.Harding@Sun.COM static void
yge_set_rambuffer(yge_port_t * port)263110506SBarry.Harding@Sun.COM yge_set_rambuffer(yge_port_t *port)
263210506SBarry.Harding@Sun.COM {
263310506SBarry.Harding@Sun.COM 	yge_dev_t *dev;
263410506SBarry.Harding@Sun.COM 	int ltpp, utpp;
263510506SBarry.Harding@Sun.COM 	int pnum;
263610506SBarry.Harding@Sun.COM 	uint32_t rxq;
263710506SBarry.Harding@Sun.COM 	uint32_t txq;
263810506SBarry.Harding@Sun.COM 
263910506SBarry.Harding@Sun.COM 	dev = port->p_dev;
264010506SBarry.Harding@Sun.COM 	pnum = port->p_port;
264110506SBarry.Harding@Sun.COM 	rxq = port->p_rxq;
264210506SBarry.Harding@Sun.COM 	txq = port->p_txq;
264310506SBarry.Harding@Sun.COM 
264410506SBarry.Harding@Sun.COM 	if ((port->p_flags & PORT_FLAG_RAMBUF) == 0)
264510506SBarry.Harding@Sun.COM 		return;
264610506SBarry.Harding@Sun.COM 
264710506SBarry.Harding@Sun.COM 	/* Setup Rx Queue. */
264810506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_RST_CLR);
264910506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_START), dev->d_rxqstart[pnum] / 8);
265010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_END), dev->d_rxqend[pnum] / 8);
265110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_WP), dev->d_rxqstart[pnum] / 8);
265210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RP), dev->d_rxqstart[pnum] / 8);
265310506SBarry.Harding@Sun.COM 
265410506SBarry.Harding@Sun.COM 	utpp =
265510506SBarry.Harding@Sun.COM 	    (dev->d_rxqend[pnum] + 1 - dev->d_rxqstart[pnum] - RB_ULPP) / 8;
265610506SBarry.Harding@Sun.COM 	ltpp =
265710506SBarry.Harding@Sun.COM 	    (dev->d_rxqend[pnum] + 1 - dev->d_rxqstart[pnum] - RB_LLPP_B) / 8;
265810506SBarry.Harding@Sun.COM 
265910506SBarry.Harding@Sun.COM 	if (dev->d_rxqsize < MSK_MIN_RXQ_SIZE)
266010506SBarry.Harding@Sun.COM 		ltpp += (RB_LLPP_B - RB_LLPP_S) / 8;
266110506SBarry.Harding@Sun.COM 
266210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RX_UTPP), utpp);
266310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RX_LTPP), ltpp);
266410506SBarry.Harding@Sun.COM 	/* Set Rx priority(RB_RX_UTHP/RB_RX_LTHP) thresholds? */
266510506SBarry.Harding@Sun.COM 
266610506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_ENA_OP_MD);
266710506SBarry.Harding@Sun.COM 	(void) CSR_READ_1(dev, RB_ADDR(rxq, RB_CTRL));
266810506SBarry.Harding@Sun.COM 
266910506SBarry.Harding@Sun.COM 	/* Setup Tx Queue. */
267010506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_CLR);
267110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_START), dev->d_txqstart[pnum] / 8);
267210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_END),  dev->d_txqend[pnum] / 8);
267310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_WP), dev->d_txqstart[pnum] / 8);
267410506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_RP), dev->d_txqstart[pnum] / 8);
267510506SBarry.Harding@Sun.COM 	/* Enable Store & Forward for Tx side. */
267610506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_ENA_STFWD);
267710506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_ENA_OP_MD);
267810506SBarry.Harding@Sun.COM 	(void) CSR_READ_1(dev, RB_ADDR(txq, RB_CTRL));
267910506SBarry.Harding@Sun.COM }
268010506SBarry.Harding@Sun.COM 
268110506SBarry.Harding@Sun.COM static void
yge_set_prefetch(yge_dev_t * dev,int qaddr,yge_ring_t * ring)268210506SBarry.Harding@Sun.COM yge_set_prefetch(yge_dev_t *dev, int qaddr, yge_ring_t *ring)
268310506SBarry.Harding@Sun.COM {
268410506SBarry.Harding@Sun.COM 	/* Reset the prefetch unit. */
268510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
268610506SBarry.Harding@Sun.COM 	    PREF_UNIT_RST_SET);
268710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
268810506SBarry.Harding@Sun.COM 	    PREF_UNIT_RST_CLR);
268910506SBarry.Harding@Sun.COM 	/* Set LE base address. */
269010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_ADDR_LOW_REG),
269110506SBarry.Harding@Sun.COM 	    YGE_ADDR_LO(ring->r_paddr));
269210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_ADDR_HI_REG),
269310506SBarry.Harding@Sun.COM 	    YGE_ADDR_HI(ring->r_paddr));
269410506SBarry.Harding@Sun.COM 	/* Set the list last index. */
269510506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_LAST_IDX_REG),
269610506SBarry.Harding@Sun.COM 	    ring->r_num - 1);
269710506SBarry.Harding@Sun.COM 	/* Turn on prefetch unit. */
269810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
269910506SBarry.Harding@Sun.COM 	    PREF_UNIT_OP_ON);
270010506SBarry.Harding@Sun.COM 	/* Dummy read to ensure write. */
270110506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG));
270210506SBarry.Harding@Sun.COM }
270310506SBarry.Harding@Sun.COM 
270410506SBarry.Harding@Sun.COM static void
yge_stop_port(yge_port_t * port)270510506SBarry.Harding@Sun.COM yge_stop_port(yge_port_t *port)
270610506SBarry.Harding@Sun.COM {
270710506SBarry.Harding@Sun.COM 	yge_dev_t *dev = port->p_dev;
270810506SBarry.Harding@Sun.COM 	int pnum = port->p_port;
270910506SBarry.Harding@Sun.COM 	uint32_t txq = port->p_txq;
271010506SBarry.Harding@Sun.COM 	uint32_t rxq = port->p_rxq;
271110506SBarry.Harding@Sun.COM 	uint32_t val;
271210506SBarry.Harding@Sun.COM 	int i;
271310506SBarry.Harding@Sun.COM 
271410506SBarry.Harding@Sun.COM 	dev = port->p_dev;
271510506SBarry.Harding@Sun.COM 
271610506SBarry.Harding@Sun.COM 	/*
271710506SBarry.Harding@Sun.COM 	 * shutdown timeout
271810506SBarry.Harding@Sun.COM 	 */
271910506SBarry.Harding@Sun.COM 	port->p_tx_wdog = 0;
272010506SBarry.Harding@Sun.COM 
272110506SBarry.Harding@Sun.COM 	/* Disable interrupts. */
272210506SBarry.Harding@Sun.COM 	if (pnum == YGE_PORT_A) {
272310506SBarry.Harding@Sun.COM 		dev->d_intrmask &= ~Y2_IS_PORT_A;
272410506SBarry.Harding@Sun.COM 		dev->d_intrhwemask &= ~Y2_HWE_L1_MASK;
272510506SBarry.Harding@Sun.COM 	} else {
272610506SBarry.Harding@Sun.COM 		dev->d_intrmask &= ~Y2_IS_PORT_B;
272710506SBarry.Harding@Sun.COM 		dev->d_intrhwemask &= ~Y2_HWE_L2_MASK;
272810506SBarry.Harding@Sun.COM 	}
272910506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, dev->d_intrhwemask);
273010506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
273110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
273210506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
273310506SBarry.Harding@Sun.COM 
273410506SBarry.Harding@Sun.COM 	/* Disable Tx/Rx MAC. */
273510506SBarry.Harding@Sun.COM 	val = GMAC_READ_2(dev, pnum, GM_GP_CTRL);
273610506SBarry.Harding@Sun.COM 	val &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
273710506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_GP_CTRL, val);
273810506SBarry.Harding@Sun.COM 	/* Read again to ensure writing. */
273910506SBarry.Harding@Sun.COM 	(void) GMAC_READ_2(dev, pnum, GM_GP_CTRL);
274010506SBarry.Harding@Sun.COM 
274110506SBarry.Harding@Sun.COM 	/* Update stats and clear counters. */
274210506SBarry.Harding@Sun.COM 	yge_stats_update(port);
274310506SBarry.Harding@Sun.COM 
274410506SBarry.Harding@Sun.COM 	/* Stop Tx BMU. */
274510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_STOP);
274610506SBarry.Harding@Sun.COM 	val = CSR_READ_4(dev, Q_ADDR(txq, Q_CSR));
274710506SBarry.Harding@Sun.COM 	for (i = 0; i < YGE_TIMEOUT; i += 10) {
274810506SBarry.Harding@Sun.COM 		if ((val & (BMU_STOP | BMU_IDLE)) == 0) {
274910506SBarry.Harding@Sun.COM 			CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_STOP);
275010506SBarry.Harding@Sun.COM 			val = CSR_READ_4(dev, Q_ADDR(txq, Q_CSR));
275110506SBarry.Harding@Sun.COM 		} else
275210506SBarry.Harding@Sun.COM 			break;
275310506SBarry.Harding@Sun.COM 		drv_usecwait(10);
275410506SBarry.Harding@Sun.COM 	}
275510506SBarry.Harding@Sun.COM 	/* This is probably fairly catastrophic. */
275610506SBarry.Harding@Sun.COM 	if ((val & (BMU_STOP | BMU_IDLE)) == 0)
275710506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Tx BMU stop failed");
275810506SBarry.Harding@Sun.COM 
275910506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_SET | RB_DIS_OP_MD);
276010506SBarry.Harding@Sun.COM 
276110506SBarry.Harding@Sun.COM 	/* Disable all GMAC interrupt. */
276210506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, MR_ADDR(pnum, GMAC_IRQ_MSK), 0);
276310506SBarry.Harding@Sun.COM 
276410506SBarry.Harding@Sun.COM 	/* Disable the RAM Interface Arbiter. */
276510506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL), TXA_DIS_ARB);
276610506SBarry.Harding@Sun.COM 
276710506SBarry.Harding@Sun.COM 	/* Reset the PCI FIFO of the async Tx queue */
276810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
276910506SBarry.Harding@Sun.COM 
277010506SBarry.Harding@Sun.COM 	/* Reset the Tx prefetch units. */
277110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(txq, PREF_UNIT_CTRL_REG),
277210506SBarry.Harding@Sun.COM 	    PREF_UNIT_RST_SET);
277310506SBarry.Harding@Sun.COM 
277410506SBarry.Harding@Sun.COM 	/* Reset the RAM Buffer async Tx queue. */
277510506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_SET);
277610506SBarry.Harding@Sun.COM 
277710506SBarry.Harding@Sun.COM 	/* Reset Tx MAC FIFO. */
277810506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_SET);
277910506SBarry.Harding@Sun.COM 	/* Set Pause Off. */
278010506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_PAUSE_OFF);
278110506SBarry.Harding@Sun.COM 
278210506SBarry.Harding@Sun.COM 	/*
278310506SBarry.Harding@Sun.COM 	 * The Rx Stop command will not work for Yukon-2 if the BMU does not
278410506SBarry.Harding@Sun.COM 	 * reach the end of packet and since we can't make sure that we have
278510506SBarry.Harding@Sun.COM 	 * incoming data, we must reset the BMU while it is not during a DMA
278610506SBarry.Harding@Sun.COM 	 * transfer. Since it is possible that the Rx path is still active,
278710506SBarry.Harding@Sun.COM 	 * the Rx RAM buffer will be stopped first, so any possible incoming
278810506SBarry.Harding@Sun.COM 	 * data will not trigger a DMA. After the RAM buffer is stopped, the
278910506SBarry.Harding@Sun.COM 	 * BMU is polled until any DMA in progress is ended and only then it
279010506SBarry.Harding@Sun.COM 	 * will be reset.
279110506SBarry.Harding@Sun.COM 	 */
279210506SBarry.Harding@Sun.COM 
279310506SBarry.Harding@Sun.COM 	/* Disable the RAM Buffer receive queue. */
279410506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
279510506SBarry.Harding@Sun.COM 	for (i = 0; i < YGE_TIMEOUT; i += 10) {
279610506SBarry.Harding@Sun.COM 		if (CSR_READ_1(dev, RB_ADDR(rxq, Q_RSL)) ==
279710506SBarry.Harding@Sun.COM 		    CSR_READ_1(dev, RB_ADDR(rxq, Q_RL)))
279810506SBarry.Harding@Sun.COM 			break;
279910506SBarry.Harding@Sun.COM 		drv_usecwait(10);
280010506SBarry.Harding@Sun.COM 	}
280110506SBarry.Harding@Sun.COM 	/* This is probably nearly a fatal error. */
280210506SBarry.Harding@Sun.COM 	if (i == YGE_TIMEOUT)
280310506SBarry.Harding@Sun.COM 		yge_error(NULL, port, "Rx BMU stop failed");
280410506SBarry.Harding@Sun.COM 
280510506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
280610506SBarry.Harding@Sun.COM 	/* Reset the Rx prefetch unit. */
280710506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(rxq, PREF_UNIT_CTRL_REG),
280810506SBarry.Harding@Sun.COM 	    PREF_UNIT_RST_SET);
280910506SBarry.Harding@Sun.COM 	/* Reset the RAM Buffer receive queue. */
281010506SBarry.Harding@Sun.COM 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_RST_SET);
281110506SBarry.Harding@Sun.COM 	/* Reset Rx MAC FIFO. */
281210506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_SET);
281310506SBarry.Harding@Sun.COM }
281410506SBarry.Harding@Sun.COM 
281510506SBarry.Harding@Sun.COM /*
281610506SBarry.Harding@Sun.COM  * When GM_PAR_MIB_CLR bit of GM_PHY_ADDR is set, reading lower
281710506SBarry.Harding@Sun.COM  * counter clears high 16 bits of the counter such that accessing
281810506SBarry.Harding@Sun.COM  * lower 16 bits should be the last operation.
281910506SBarry.Harding@Sun.COM  */
282010506SBarry.Harding@Sun.COM #define	YGE_READ_MIB32(x, y)					\
282110506SBarry.Harding@Sun.COM 	GMAC_READ_4(dev, x, y)
282210506SBarry.Harding@Sun.COM 
282310506SBarry.Harding@Sun.COM #define	YGE_READ_MIB64(x, y)					\
282410506SBarry.Harding@Sun.COM 	((((uint64_t)YGE_READ_MIB32(x, (y) + 8)) << 32) +	\
282510506SBarry.Harding@Sun.COM 	    (uint64_t)YGE_READ_MIB32(x, y))
282610506SBarry.Harding@Sun.COM 
282710506SBarry.Harding@Sun.COM static void
yge_stats_clear(yge_port_t * port)282810506SBarry.Harding@Sun.COM yge_stats_clear(yge_port_t *port)
282910506SBarry.Harding@Sun.COM {
283010506SBarry.Harding@Sun.COM 	yge_dev_t *dev;
283110506SBarry.Harding@Sun.COM 	uint16_t gmac;
283210506SBarry.Harding@Sun.COM 	int32_t pnum;
283310506SBarry.Harding@Sun.COM 
283410506SBarry.Harding@Sun.COM 	pnum = port->p_port;
283510506SBarry.Harding@Sun.COM 	dev = port->p_dev;
283610506SBarry.Harding@Sun.COM 
283710506SBarry.Harding@Sun.COM 	/* Set MIB Clear Counter Mode. */
283810506SBarry.Harding@Sun.COM 	gmac = GMAC_READ_2(dev, pnum, GM_PHY_ADDR);
283910506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR);
284010506SBarry.Harding@Sun.COM 	/* Read all MIB Counters with Clear Mode set. */
284110506SBarry.Harding@Sun.COM 	for (int i = GM_RXF_UC_OK; i <= GM_TXE_FIFO_UR; i += 4)
284210506SBarry.Harding@Sun.COM 		(void) YGE_READ_MIB32(pnum, i);
284310506SBarry.Harding@Sun.COM 	/* Clear MIB Clear Counter Mode. */
284410506SBarry.Harding@Sun.COM 	gmac &= ~GM_PAR_MIB_CLR;
284510506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac);
284610506SBarry.Harding@Sun.COM }
284710506SBarry.Harding@Sun.COM 
284810506SBarry.Harding@Sun.COM static void
yge_stats_update(yge_port_t * port)284910506SBarry.Harding@Sun.COM yge_stats_update(yge_port_t *port)
285010506SBarry.Harding@Sun.COM {
285110506SBarry.Harding@Sun.COM 	yge_dev_t *dev;
285210506SBarry.Harding@Sun.COM 	struct yge_hw_stats *stats;
285310506SBarry.Harding@Sun.COM 	uint16_t gmac;
285410506SBarry.Harding@Sun.COM 	int32_t	pnum;
285510506SBarry.Harding@Sun.COM 
285610506SBarry.Harding@Sun.COM 	dev = port->p_dev;
285710506SBarry.Harding@Sun.COM 	pnum = port->p_port;
285810506SBarry.Harding@Sun.COM 
285910506SBarry.Harding@Sun.COM 	if (dev->d_suspended || !port->p_running) {
286010506SBarry.Harding@Sun.COM 		return;
286110506SBarry.Harding@Sun.COM 	}
286210506SBarry.Harding@Sun.COM 	stats = &port->p_stats;
286310506SBarry.Harding@Sun.COM 	/* Set MIB Clear Counter Mode. */
286410506SBarry.Harding@Sun.COM 	gmac = GMAC_READ_2(dev, pnum, GM_PHY_ADDR);
286510506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR);
286610506SBarry.Harding@Sun.COM 
286710506SBarry.Harding@Sun.COM 	/* Rx stats. */
286810506SBarry.Harding@Sun.COM 	stats->rx_ucast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_UC_OK);
286910506SBarry.Harding@Sun.COM 	stats->rx_bcast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_BC_OK);
287010506SBarry.Harding@Sun.COM 	stats->rx_pause_frames +=	YGE_READ_MIB32(pnum, GM_RXF_MPAUSE);
287110506SBarry.Harding@Sun.COM 	stats->rx_mcast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_MC_OK);
287210506SBarry.Harding@Sun.COM 	stats->rx_crc_errs +=		YGE_READ_MIB32(pnum, GM_RXF_FCS_ERR);
287310506SBarry.Harding@Sun.COM 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE1);
287410506SBarry.Harding@Sun.COM 	stats->rx_good_octets +=	YGE_READ_MIB64(pnum, GM_RXO_OK_LO);
287510506SBarry.Harding@Sun.COM 	stats->rx_bad_octets +=		YGE_READ_MIB64(pnum, GM_RXO_ERR_LO);
287610506SBarry.Harding@Sun.COM 	stats->rx_runts +=		YGE_READ_MIB32(pnum, GM_RXF_SHT);
287710506SBarry.Harding@Sun.COM 	stats->rx_runt_errs +=		YGE_READ_MIB32(pnum, GM_RXE_FRAG);
287810506SBarry.Harding@Sun.COM 	stats->rx_pkts_64 +=		YGE_READ_MIB32(pnum, GM_RXF_64B);
287910506SBarry.Harding@Sun.COM 	stats->rx_pkts_65_127 +=	YGE_READ_MIB32(pnum, GM_RXF_127B);
288010506SBarry.Harding@Sun.COM 	stats->rx_pkts_128_255 +=	YGE_READ_MIB32(pnum, GM_RXF_255B);
288110506SBarry.Harding@Sun.COM 	stats->rx_pkts_256_511 +=	YGE_READ_MIB32(pnum, GM_RXF_511B);
288210506SBarry.Harding@Sun.COM 	stats->rx_pkts_512_1023 +=	YGE_READ_MIB32(pnum, GM_RXF_1023B);
288310506SBarry.Harding@Sun.COM 	stats->rx_pkts_1024_1518 +=	YGE_READ_MIB32(pnum, GM_RXF_1518B);
288410506SBarry.Harding@Sun.COM 	stats->rx_pkts_1519_max +=	YGE_READ_MIB32(pnum, GM_RXF_MAX_SZ);
288510506SBarry.Harding@Sun.COM 	stats->rx_pkts_too_long +=	YGE_READ_MIB32(pnum, GM_RXF_LNG_ERR);
288610506SBarry.Harding@Sun.COM 	stats->rx_pkts_jabbers +=	YGE_READ_MIB32(pnum, GM_RXF_JAB_PKT);
288710506SBarry.Harding@Sun.COM 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE2);
288810506SBarry.Harding@Sun.COM 	stats->rx_fifo_oflows +=	YGE_READ_MIB32(pnum, GM_RXE_FIFO_OV);
288910506SBarry.Harding@Sun.COM 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE3);
289010506SBarry.Harding@Sun.COM 
289110506SBarry.Harding@Sun.COM 	/* Tx stats. */
289210506SBarry.Harding@Sun.COM 	stats->tx_ucast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_UC_OK);
289310506SBarry.Harding@Sun.COM 	stats->tx_bcast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_BC_OK);
289410506SBarry.Harding@Sun.COM 	stats->tx_pause_frames +=	YGE_READ_MIB32(pnum, GM_TXF_MPAUSE);
289510506SBarry.Harding@Sun.COM 	stats->tx_mcast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_MC_OK);
289610506SBarry.Harding@Sun.COM 	stats->tx_octets +=		YGE_READ_MIB64(pnum, GM_TXO_OK_LO);
289710506SBarry.Harding@Sun.COM 	stats->tx_pkts_64 +=		YGE_READ_MIB32(pnum, GM_TXF_64B);
289810506SBarry.Harding@Sun.COM 	stats->tx_pkts_65_127 +=	YGE_READ_MIB32(pnum, GM_TXF_127B);
289910506SBarry.Harding@Sun.COM 	stats->tx_pkts_128_255 +=	YGE_READ_MIB32(pnum, GM_TXF_255B);
290010506SBarry.Harding@Sun.COM 	stats->tx_pkts_256_511 +=	YGE_READ_MIB32(pnum, GM_TXF_511B);
290110506SBarry.Harding@Sun.COM 	stats->tx_pkts_512_1023 +=	YGE_READ_MIB32(pnum, GM_TXF_1023B);
290210506SBarry.Harding@Sun.COM 	stats->tx_pkts_1024_1518 +=	YGE_READ_MIB32(pnum, GM_TXF_1518B);
290310506SBarry.Harding@Sun.COM 	stats->tx_pkts_1519_max +=	YGE_READ_MIB32(pnum, GM_TXF_MAX_SZ);
290410506SBarry.Harding@Sun.COM 	(void) YGE_READ_MIB32(pnum, GM_TXF_SPARE1);
290510506SBarry.Harding@Sun.COM 	stats->tx_colls +=		YGE_READ_MIB32(pnum, GM_TXF_COL);
290610506SBarry.Harding@Sun.COM 	stats->tx_late_colls +=		YGE_READ_MIB32(pnum, GM_TXF_LAT_COL);
290710506SBarry.Harding@Sun.COM 	stats->tx_excess_colls +=	YGE_READ_MIB32(pnum, GM_TXF_ABO_COL);
290810506SBarry.Harding@Sun.COM 	stats->tx_multi_colls +=	YGE_READ_MIB32(pnum, GM_TXF_MUL_COL);
290910506SBarry.Harding@Sun.COM 	stats->tx_single_colls +=	YGE_READ_MIB32(pnum, GM_TXF_SNG_COL);
291010506SBarry.Harding@Sun.COM 	stats->tx_underflows +=		YGE_READ_MIB32(pnum, GM_TXE_FIFO_UR);
291110506SBarry.Harding@Sun.COM 	/* Clear MIB Clear Counter Mode. */
291210506SBarry.Harding@Sun.COM 	gmac &= ~GM_PAR_MIB_CLR;
291310506SBarry.Harding@Sun.COM 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac);
291410506SBarry.Harding@Sun.COM }
291510506SBarry.Harding@Sun.COM 
291610506SBarry.Harding@Sun.COM #undef YGE_READ_MIB32
291710506SBarry.Harding@Sun.COM #undef YGE_READ_MIB64
291810506SBarry.Harding@Sun.COM 
291910506SBarry.Harding@Sun.COM uint32_t
yge_hashbit(const uint8_t * addr)292010506SBarry.Harding@Sun.COM yge_hashbit(const uint8_t *addr)
292110506SBarry.Harding@Sun.COM {
292210506SBarry.Harding@Sun.COM 	int		idx;
292310506SBarry.Harding@Sun.COM 	int		bit;
292410506SBarry.Harding@Sun.COM 	uint_t		data;
292510506SBarry.Harding@Sun.COM 	uint32_t	crc;
292610506SBarry.Harding@Sun.COM #define	POLY_BE	0x04c11db7
292710506SBarry.Harding@Sun.COM 
292810506SBarry.Harding@Sun.COM 	crc = 0xffffffff;
292910506SBarry.Harding@Sun.COM 	for (idx = 0; idx < 6; idx++) {
293010506SBarry.Harding@Sun.COM 		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
293110506SBarry.Harding@Sun.COM 			crc = (crc << 1)
293210506SBarry.Harding@Sun.COM 			    ^ ((((crc >> 31) ^ data) & 1) ? POLY_BE : 0);
293310506SBarry.Harding@Sun.COM 		}
293410506SBarry.Harding@Sun.COM 	}
293510506SBarry.Harding@Sun.COM #undef	POLY_BE
293610506SBarry.Harding@Sun.COM 
293710506SBarry.Harding@Sun.COM 	return (crc % 64);
293810506SBarry.Harding@Sun.COM }
293910506SBarry.Harding@Sun.COM 
294010506SBarry.Harding@Sun.COM int
yge_m_stat(void * arg,uint_t stat,uint64_t * val)294110506SBarry.Harding@Sun.COM yge_m_stat(void *arg, uint_t stat, uint64_t *val)
294210506SBarry.Harding@Sun.COM {
294310506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
294410506SBarry.Harding@Sun.COM 	struct yge_hw_stats *stats = &port->p_stats;
294510506SBarry.Harding@Sun.COM 
294610506SBarry.Harding@Sun.COM 	if (stat == MAC_STAT_IFSPEED) {
294710506SBarry.Harding@Sun.COM 		/*
294810506SBarry.Harding@Sun.COM 		 * This is the first stat we are asked about.  We update only
294910506SBarry.Harding@Sun.COM 		 * for this stat, to avoid paying the hefty cost of the update
295010506SBarry.Harding@Sun.COM 		 * once for each stat.
295110506SBarry.Harding@Sun.COM 		 */
295210506SBarry.Harding@Sun.COM 		DEV_LOCK(port->p_dev);
295310506SBarry.Harding@Sun.COM 		yge_stats_update(port);
295410506SBarry.Harding@Sun.COM 		DEV_UNLOCK(port->p_dev);
295510506SBarry.Harding@Sun.COM 	}
295610506SBarry.Harding@Sun.COM 
295710506SBarry.Harding@Sun.COM 	if (mii_m_getstat(port->p_mii, stat, val) == 0) {
295810506SBarry.Harding@Sun.COM 		return (0);
295910506SBarry.Harding@Sun.COM 	}
296010506SBarry.Harding@Sun.COM 
296110506SBarry.Harding@Sun.COM 	switch (stat) {
296210506SBarry.Harding@Sun.COM 	case MAC_STAT_MULTIRCV:
296310506SBarry.Harding@Sun.COM 		*val = stats->rx_mcast_frames;
296410506SBarry.Harding@Sun.COM 		break;
296510506SBarry.Harding@Sun.COM 
296610506SBarry.Harding@Sun.COM 	case MAC_STAT_BRDCSTRCV:
296710506SBarry.Harding@Sun.COM 		*val = stats->rx_bcast_frames;
296810506SBarry.Harding@Sun.COM 		break;
296910506SBarry.Harding@Sun.COM 
297010506SBarry.Harding@Sun.COM 	case MAC_STAT_MULTIXMT:
297110506SBarry.Harding@Sun.COM 		*val = stats->tx_mcast_frames;
297210506SBarry.Harding@Sun.COM 		break;
297310506SBarry.Harding@Sun.COM 
297410506SBarry.Harding@Sun.COM 	case MAC_STAT_BRDCSTXMT:
297510506SBarry.Harding@Sun.COM 		*val = stats->tx_bcast_frames;
297610506SBarry.Harding@Sun.COM 		break;
297710506SBarry.Harding@Sun.COM 
297810506SBarry.Harding@Sun.COM 	case MAC_STAT_IPACKETS:
297910506SBarry.Harding@Sun.COM 		*val = stats->rx_ucast_frames;
298010506SBarry.Harding@Sun.COM 		break;
298110506SBarry.Harding@Sun.COM 
298210506SBarry.Harding@Sun.COM 	case MAC_STAT_RBYTES:
298310506SBarry.Harding@Sun.COM 		*val = stats->rx_good_octets;
298410506SBarry.Harding@Sun.COM 		break;
298510506SBarry.Harding@Sun.COM 
298610506SBarry.Harding@Sun.COM 	case MAC_STAT_OPACKETS:
298710506SBarry.Harding@Sun.COM 		*val = stats->tx_ucast_frames;
298810506SBarry.Harding@Sun.COM 		break;
298910506SBarry.Harding@Sun.COM 
299010506SBarry.Harding@Sun.COM 	case MAC_STAT_OBYTES:
299110506SBarry.Harding@Sun.COM 		*val = stats->tx_octets;
299210506SBarry.Harding@Sun.COM 		break;
299310506SBarry.Harding@Sun.COM 
299410506SBarry.Harding@Sun.COM 	case MAC_STAT_NORCVBUF:
299510506SBarry.Harding@Sun.COM 		*val = stats->rx_nobuf;
299610506SBarry.Harding@Sun.COM 		break;
299710506SBarry.Harding@Sun.COM 
299810506SBarry.Harding@Sun.COM 	case MAC_STAT_COLLISIONS:
299910506SBarry.Harding@Sun.COM 		*val = stats->tx_colls;
300010506SBarry.Harding@Sun.COM 		break;
300110506SBarry.Harding@Sun.COM 
300210506SBarry.Harding@Sun.COM 	case ETHER_STAT_ALIGN_ERRORS:
300310506SBarry.Harding@Sun.COM 		*val = stats->rx_runt_errs;
300410506SBarry.Harding@Sun.COM 		break;
300510506SBarry.Harding@Sun.COM 
300610506SBarry.Harding@Sun.COM 	case ETHER_STAT_FCS_ERRORS:
300710506SBarry.Harding@Sun.COM 		*val = stats->rx_crc_errs;
300810506SBarry.Harding@Sun.COM 		break;
300910506SBarry.Harding@Sun.COM 
301010506SBarry.Harding@Sun.COM 	case ETHER_STAT_FIRST_COLLISIONS:
301110506SBarry.Harding@Sun.COM 		*val  = stats->tx_single_colls;
301210506SBarry.Harding@Sun.COM 		break;
301310506SBarry.Harding@Sun.COM 
301410506SBarry.Harding@Sun.COM 	case ETHER_STAT_MULTI_COLLISIONS:
301510506SBarry.Harding@Sun.COM 		*val = stats->tx_multi_colls;
301610506SBarry.Harding@Sun.COM 		break;
301710506SBarry.Harding@Sun.COM 
301810506SBarry.Harding@Sun.COM 	case ETHER_STAT_TX_LATE_COLLISIONS:
301910506SBarry.Harding@Sun.COM 		*val = stats->tx_late_colls;
302010506SBarry.Harding@Sun.COM 		break;
302110506SBarry.Harding@Sun.COM 
302210506SBarry.Harding@Sun.COM 	case ETHER_STAT_EX_COLLISIONS:
302310506SBarry.Harding@Sun.COM 		*val = stats->tx_excess_colls;
302410506SBarry.Harding@Sun.COM 		break;
302510506SBarry.Harding@Sun.COM 
302610506SBarry.Harding@Sun.COM 	case ETHER_STAT_TOOLONG_ERRORS:
302710506SBarry.Harding@Sun.COM 		*val = stats->rx_pkts_too_long;
302810506SBarry.Harding@Sun.COM 		break;
302910506SBarry.Harding@Sun.COM 
303010506SBarry.Harding@Sun.COM 	case MAC_STAT_OVERFLOWS:
303110506SBarry.Harding@Sun.COM 		*val = stats->rx_fifo_oflows;
303210506SBarry.Harding@Sun.COM 		break;
303310506SBarry.Harding@Sun.COM 
303410506SBarry.Harding@Sun.COM 	case MAC_STAT_UNDERFLOWS:
303510506SBarry.Harding@Sun.COM 		*val = stats->tx_underflows;
303610506SBarry.Harding@Sun.COM 		break;
303710506SBarry.Harding@Sun.COM 
303810506SBarry.Harding@Sun.COM 	case ETHER_STAT_TOOSHORT_ERRORS:
303910506SBarry.Harding@Sun.COM 		*val = stats->rx_runts;
304010506SBarry.Harding@Sun.COM 		break;
304110506SBarry.Harding@Sun.COM 
304210506SBarry.Harding@Sun.COM 	case ETHER_STAT_JABBER_ERRORS:
304310506SBarry.Harding@Sun.COM 		*val = stats->rx_pkts_jabbers;
304410506SBarry.Harding@Sun.COM 		break;
304510506SBarry.Harding@Sun.COM 
304610506SBarry.Harding@Sun.COM 	default:
304710506SBarry.Harding@Sun.COM 		return (ENOTSUP);
304810506SBarry.Harding@Sun.COM 	}
304910506SBarry.Harding@Sun.COM 	return (0);
305010506SBarry.Harding@Sun.COM }
305110506SBarry.Harding@Sun.COM 
305210506SBarry.Harding@Sun.COM int
yge_m_start(void * arg)305310506SBarry.Harding@Sun.COM yge_m_start(void *arg)
305410506SBarry.Harding@Sun.COM {
305510506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
305610506SBarry.Harding@Sun.COM 
305710506SBarry.Harding@Sun.COM 	DEV_LOCK(port->p_dev);
305810506SBarry.Harding@Sun.COM 
305910506SBarry.Harding@Sun.COM 	/*
306010506SBarry.Harding@Sun.COM 	 * We defer resource allocation to this point, because we
306110506SBarry.Harding@Sun.COM 	 * don't want to waste DMA resources that might better be used
306210506SBarry.Harding@Sun.COM 	 * elsewhere, if the port is not actually being used.
306310506SBarry.Harding@Sun.COM 	 *
306410506SBarry.Harding@Sun.COM 	 * Furthermore, this gives us a more graceful handling of dynamic
306510506SBarry.Harding@Sun.COM 	 * MTU modification.
306610506SBarry.Harding@Sun.COM 	 */
306710506SBarry.Harding@Sun.COM 	if (yge_txrx_dma_alloc(port) != DDI_SUCCESS) {
306810506SBarry.Harding@Sun.COM 		/* Make sure we free up partially allocated resources. */
306910506SBarry.Harding@Sun.COM 		yge_txrx_dma_free(port);
307010506SBarry.Harding@Sun.COM 		DEV_UNLOCK(port->p_dev);
307110506SBarry.Harding@Sun.COM 		return (ENOMEM);
307210506SBarry.Harding@Sun.COM 	}
307310506SBarry.Harding@Sun.COM 
307410506SBarry.Harding@Sun.COM 	if (!port->p_dev->d_suspended)
307510506SBarry.Harding@Sun.COM 		yge_start_port(port);
307610506SBarry.Harding@Sun.COM 	port->p_running = B_TRUE;
307710506SBarry.Harding@Sun.COM 	DEV_UNLOCK(port->p_dev);
307810506SBarry.Harding@Sun.COM 
307910506SBarry.Harding@Sun.COM 	mii_start(port->p_mii);
308010506SBarry.Harding@Sun.COM 
308110506SBarry.Harding@Sun.COM 	return (0);
308210506SBarry.Harding@Sun.COM }
308310506SBarry.Harding@Sun.COM 
308410506SBarry.Harding@Sun.COM void
yge_m_stop(void * arg)308510506SBarry.Harding@Sun.COM yge_m_stop(void *arg)
308610506SBarry.Harding@Sun.COM {
308710506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
308810506SBarry.Harding@Sun.COM 	yge_dev_t	*dev = port->p_dev;
308910506SBarry.Harding@Sun.COM 
309010506SBarry.Harding@Sun.COM 	DEV_LOCK(dev);
309110506SBarry.Harding@Sun.COM 	if (!dev->d_suspended)
309210506SBarry.Harding@Sun.COM 		yge_stop_port(port);
309310506SBarry.Harding@Sun.COM 
309410506SBarry.Harding@Sun.COM 	port->p_running = B_FALSE;
309510506SBarry.Harding@Sun.COM 
309610506SBarry.Harding@Sun.COM 	/* Release resources we don't need */
309710506SBarry.Harding@Sun.COM 	yge_txrx_dma_free(port);
309810506SBarry.Harding@Sun.COM 	DEV_UNLOCK(dev);
309910506SBarry.Harding@Sun.COM }
310010506SBarry.Harding@Sun.COM 
310110506SBarry.Harding@Sun.COM int
yge_m_promisc(void * arg,boolean_t on)310210506SBarry.Harding@Sun.COM yge_m_promisc(void *arg, boolean_t on)
310310506SBarry.Harding@Sun.COM {
310410506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
310510506SBarry.Harding@Sun.COM 
310610506SBarry.Harding@Sun.COM 	DEV_LOCK(port->p_dev);
310710506SBarry.Harding@Sun.COM 
310810506SBarry.Harding@Sun.COM 	/* Save current promiscuous mode. */
310910506SBarry.Harding@Sun.COM 	port->p_promisc = on;
311010506SBarry.Harding@Sun.COM 	yge_setrxfilt(port);
311110506SBarry.Harding@Sun.COM 
311210506SBarry.Harding@Sun.COM 	DEV_UNLOCK(port->p_dev);
311310506SBarry.Harding@Sun.COM 
311410506SBarry.Harding@Sun.COM 	return (0);
311510506SBarry.Harding@Sun.COM }
311610506SBarry.Harding@Sun.COM 
311710506SBarry.Harding@Sun.COM int
yge_m_multicst(void * arg,boolean_t add,const uint8_t * addr)311810506SBarry.Harding@Sun.COM yge_m_multicst(void *arg, boolean_t add, const uint8_t *addr)
311910506SBarry.Harding@Sun.COM {
312010506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
312110506SBarry.Harding@Sun.COM 	int		bit;
312210506SBarry.Harding@Sun.COM 	boolean_t	update;
312310506SBarry.Harding@Sun.COM 
312410506SBarry.Harding@Sun.COM 	bit = yge_hashbit(addr);
312510506SBarry.Harding@Sun.COM 	ASSERT(bit < 64);
312610506SBarry.Harding@Sun.COM 
312710506SBarry.Harding@Sun.COM 	DEV_LOCK(port->p_dev);
312810506SBarry.Harding@Sun.COM 	if (add) {
312910506SBarry.Harding@Sun.COM 		if (port->p_mccount[bit] == 0) {
313010506SBarry.Harding@Sun.COM 			/* Set the corresponding bit in the hash table. */
313110506SBarry.Harding@Sun.COM 			port->p_mchash[bit / 32] |= (1 << (bit % 32));
313210506SBarry.Harding@Sun.COM 			update = B_TRUE;
313310506SBarry.Harding@Sun.COM 		}
313410506SBarry.Harding@Sun.COM 		port->p_mccount[bit]++;
313510506SBarry.Harding@Sun.COM 	} else {
313610506SBarry.Harding@Sun.COM 		ASSERT(port->p_mccount[bit] > 0);
313710506SBarry.Harding@Sun.COM 		port->p_mccount[bit]--;
313810506SBarry.Harding@Sun.COM 		if (port->p_mccount[bit] == 0) {
313910506SBarry.Harding@Sun.COM 			port->p_mchash[bit / 32] &= ~(1 << (bit % 32));
314010506SBarry.Harding@Sun.COM 			update = B_TRUE;
314110506SBarry.Harding@Sun.COM 		}
314210506SBarry.Harding@Sun.COM 	}
314310506SBarry.Harding@Sun.COM 
314410506SBarry.Harding@Sun.COM 	if (update) {
314510506SBarry.Harding@Sun.COM 		yge_setrxfilt(port);
314610506SBarry.Harding@Sun.COM 	}
314710506SBarry.Harding@Sun.COM 	DEV_UNLOCK(port->p_dev);
314810506SBarry.Harding@Sun.COM 	return (0);
314910506SBarry.Harding@Sun.COM }
315010506SBarry.Harding@Sun.COM 
315110506SBarry.Harding@Sun.COM int
yge_m_unicst(void * arg,const uint8_t * macaddr)315210506SBarry.Harding@Sun.COM yge_m_unicst(void *arg, const uint8_t *macaddr)
315310506SBarry.Harding@Sun.COM {
315410506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
315510506SBarry.Harding@Sun.COM 
315610506SBarry.Harding@Sun.COM 	DEV_LOCK(port->p_dev);
315710506SBarry.Harding@Sun.COM 
315810506SBarry.Harding@Sun.COM 	bcopy(macaddr, port->p_curraddr, ETHERADDRL);
315910506SBarry.Harding@Sun.COM 	yge_setrxfilt(port);
316010506SBarry.Harding@Sun.COM 
316110506SBarry.Harding@Sun.COM 	DEV_UNLOCK(port->p_dev);
316210506SBarry.Harding@Sun.COM 
316310506SBarry.Harding@Sun.COM 	return (0);
316410506SBarry.Harding@Sun.COM }
316510506SBarry.Harding@Sun.COM 
316610506SBarry.Harding@Sun.COM mblk_t *
yge_m_tx(void * arg,mblk_t * mp)316710506SBarry.Harding@Sun.COM yge_m_tx(void *arg, mblk_t *mp)
316810506SBarry.Harding@Sun.COM {
316910506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
317010506SBarry.Harding@Sun.COM 	mblk_t		*nmp;
317110506SBarry.Harding@Sun.COM 	int		enq = 0;
317210506SBarry.Harding@Sun.COM 	uint32_t	ridx;
317310506SBarry.Harding@Sun.COM 	int		idx;
317410506SBarry.Harding@Sun.COM 	boolean_t	resched = B_FALSE;
317510506SBarry.Harding@Sun.COM 
317610506SBarry.Harding@Sun.COM 	TX_LOCK(port->p_dev);
317710506SBarry.Harding@Sun.COM 
317810506SBarry.Harding@Sun.COM 	if (port->p_dev->d_suspended) {
317910506SBarry.Harding@Sun.COM 
318010506SBarry.Harding@Sun.COM 		TX_UNLOCK(port->p_dev);
318110506SBarry.Harding@Sun.COM 
318210506SBarry.Harding@Sun.COM 		while ((nmp = mp) != NULL) {
318310506SBarry.Harding@Sun.COM 			/* carrier_errors++; */
318410506SBarry.Harding@Sun.COM 			mp = mp->b_next;
318510506SBarry.Harding@Sun.COM 			freemsg(nmp);
318610506SBarry.Harding@Sun.COM 		}
318710506SBarry.Harding@Sun.COM 		return (NULL);
318810506SBarry.Harding@Sun.COM 	}
318910506SBarry.Harding@Sun.COM 
319010506SBarry.Harding@Sun.COM 	/* attempt a reclaim */
319110506SBarry.Harding@Sun.COM 	ridx = port->p_port == YGE_PORT_A ?
319210506SBarry.Harding@Sun.COM 	    STAT_TXA1_RIDX : STAT_TXA2_RIDX;
319310506SBarry.Harding@Sun.COM 	idx = CSR_READ_2(port->p_dev, ridx);
319410506SBarry.Harding@Sun.COM 	if (port->p_tx_cons != idx)
319510506SBarry.Harding@Sun.COM 		resched = yge_txeof_locked(port, idx);
319610506SBarry.Harding@Sun.COM 
319710506SBarry.Harding@Sun.COM 	while (mp != NULL) {
319810506SBarry.Harding@Sun.COM 		nmp = mp->b_next;
319910506SBarry.Harding@Sun.COM 		mp->b_next = NULL;
320010506SBarry.Harding@Sun.COM 
320110506SBarry.Harding@Sun.COM 		if (!yge_send(port, mp)) {
320210506SBarry.Harding@Sun.COM 			mp->b_next = nmp;
320310506SBarry.Harding@Sun.COM 			break;
320410506SBarry.Harding@Sun.COM 		}
320510506SBarry.Harding@Sun.COM 		enq++;
320610506SBarry.Harding@Sun.COM 		mp = nmp;
320710506SBarry.Harding@Sun.COM 
320810506SBarry.Harding@Sun.COM 	}
320910506SBarry.Harding@Sun.COM 	if (enq > 0) {
321010506SBarry.Harding@Sun.COM 		/* Transmit */
321110506SBarry.Harding@Sun.COM 		CSR_WRITE_2(port->p_dev,
321210506SBarry.Harding@Sun.COM 		    Y2_PREF_Q_ADDR(port->p_txq, PREF_UNIT_PUT_IDX_REG),
321310506SBarry.Harding@Sun.COM 		    port->p_tx_prod);
321410506SBarry.Harding@Sun.COM 	}
321510506SBarry.Harding@Sun.COM 
321610506SBarry.Harding@Sun.COM 	TX_UNLOCK(port->p_dev);
321710506SBarry.Harding@Sun.COM 
321810506SBarry.Harding@Sun.COM 	if (resched)
321910506SBarry.Harding@Sun.COM 		mac_tx_update(port->p_mh);
322010506SBarry.Harding@Sun.COM 
322110506SBarry.Harding@Sun.COM 	return (mp);
322210506SBarry.Harding@Sun.COM }
322310506SBarry.Harding@Sun.COM 
322410506SBarry.Harding@Sun.COM void
yge_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)322510506SBarry.Harding@Sun.COM yge_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
322610506SBarry.Harding@Sun.COM {
322710506SBarry.Harding@Sun.COM #ifdef	YGE_MII_LOOPBACK
322810506SBarry.Harding@Sun.COM 	/* LINTED E_FUNC_SET_NOT_USED */
322910506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
323010506SBarry.Harding@Sun.COM 
323110506SBarry.Harding@Sun.COM 	/*
323210506SBarry.Harding@Sun.COM 	 * Right now, the MII common layer does not properly handle
323310506SBarry.Harding@Sun.COM 	 * loopback on these PHYs.  Fixing this should be done at some
323410506SBarry.Harding@Sun.COM 	 * point in the future.
323510506SBarry.Harding@Sun.COM 	 */
323610506SBarry.Harding@Sun.COM 	if (mii_m_loop_ioctl(port->p_mii, wq, mp))
323710506SBarry.Harding@Sun.COM 		return;
323810506SBarry.Harding@Sun.COM #else
323910506SBarry.Harding@Sun.COM 	_NOTE(ARGUNUSED(arg));
324010506SBarry.Harding@Sun.COM #endif
324110506SBarry.Harding@Sun.COM 
324210506SBarry.Harding@Sun.COM 	miocnak(wq, mp, 0, EINVAL);
324310506SBarry.Harding@Sun.COM }
324410506SBarry.Harding@Sun.COM 
324510506SBarry.Harding@Sun.COM int
yge_m_setprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)324610506SBarry.Harding@Sun.COM yge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
324710506SBarry.Harding@Sun.COM     uint_t pr_valsize, const void *pr_val)
324810506SBarry.Harding@Sun.COM {
324910506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
325010506SBarry.Harding@Sun.COM 	uint32_t	new_mtu;
325110506SBarry.Harding@Sun.COM 	int err = 0;
325210506SBarry.Harding@Sun.COM 
325310506SBarry.Harding@Sun.COM 	err = mii_m_setprop(port->p_mii, pr_name, pr_num, pr_valsize, pr_val);
325410506SBarry.Harding@Sun.COM 	if (err != ENOTSUP) {
325510506SBarry.Harding@Sun.COM 		return (err);
325610506SBarry.Harding@Sun.COM 	}
325710506SBarry.Harding@Sun.COM 
325810506SBarry.Harding@Sun.COM 	DEV_LOCK(port->p_dev);
325910506SBarry.Harding@Sun.COM 
326010506SBarry.Harding@Sun.COM 	switch (pr_num) {
326110506SBarry.Harding@Sun.COM 	case MAC_PROP_MTU:
326210506SBarry.Harding@Sun.COM 		if (pr_valsize < sizeof (new_mtu)) {
326310506SBarry.Harding@Sun.COM 			err = EINVAL;
326410506SBarry.Harding@Sun.COM 			break;
326510506SBarry.Harding@Sun.COM 		}
326610506SBarry.Harding@Sun.COM 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
326710506SBarry.Harding@Sun.COM 		if (new_mtu == port->p_mtu) {
326810506SBarry.Harding@Sun.COM 			/* no change */
326910506SBarry.Harding@Sun.COM 			err = 0;
327010506SBarry.Harding@Sun.COM 			break;
327110506SBarry.Harding@Sun.COM 		}
327210506SBarry.Harding@Sun.COM 		if (new_mtu < ETHERMTU) {
327310506SBarry.Harding@Sun.COM 			yge_error(NULL, port,
327410506SBarry.Harding@Sun.COM 			    "Maximum MTU size too small: %d", new_mtu);
327510506SBarry.Harding@Sun.COM 			err = EINVAL;
327610506SBarry.Harding@Sun.COM 			break;
327710506SBarry.Harding@Sun.COM 		}
327810506SBarry.Harding@Sun.COM 		if (new_mtu > (port->p_flags & PORT_FLAG_NOJUMBO ?
327910506SBarry.Harding@Sun.COM 		    ETHERMTU : YGE_JUMBO_MTU)) {
328010506SBarry.Harding@Sun.COM 			yge_error(NULL, port,
328110506SBarry.Harding@Sun.COM 			    "Maximum MTU size too big: %d", new_mtu);
328210506SBarry.Harding@Sun.COM 			err = EINVAL;
328310506SBarry.Harding@Sun.COM 			break;
328410506SBarry.Harding@Sun.COM 		}
328510506SBarry.Harding@Sun.COM 		if (port->p_running) {
328610506SBarry.Harding@Sun.COM 			yge_error(NULL, port,
328710506SBarry.Harding@Sun.COM 			    "Unable to change maximum MTU while running");
328810506SBarry.Harding@Sun.COM 			err = EBUSY;
328910506SBarry.Harding@Sun.COM 			break;
329010506SBarry.Harding@Sun.COM 		}
329110506SBarry.Harding@Sun.COM 
329210506SBarry.Harding@Sun.COM 
329310506SBarry.Harding@Sun.COM 		/*
329410506SBarry.Harding@Sun.COM 		 * NB: It would probably be better not to hold the
329510506SBarry.Harding@Sun.COM 		 * DEVLOCK, but releasing it creates a potential race
329610506SBarry.Harding@Sun.COM 		 * if m_start is called concurrently.
329710506SBarry.Harding@Sun.COM 		 *
329810506SBarry.Harding@Sun.COM 		 * It turns out that the MAC layer guarantees safety
329910506SBarry.Harding@Sun.COM 		 * for us here by using a cut out for this kind of
330010506SBarry.Harding@Sun.COM 		 * notification call back anyway.
330110506SBarry.Harding@Sun.COM 		 *
330210506SBarry.Harding@Sun.COM 		 * See R8. and R14. in mac.c locking comments, which read
330310506SBarry.Harding@Sun.COM 		 * as follows:
330410506SBarry.Harding@Sun.COM 		 *
330510506SBarry.Harding@Sun.COM 		 * R8. Since it is not guaranteed (see R14) that
330610506SBarry.Harding@Sun.COM 		 * drivers won't hold locks across mac driver
330710506SBarry.Harding@Sun.COM 		 * interfaces, the MAC layer must provide a cut out
330810506SBarry.Harding@Sun.COM 		 * for control interfaces like upcall notifications
330910506SBarry.Harding@Sun.COM 		 * and start them in a separate thread.
331010506SBarry.Harding@Sun.COM 		 *
331110506SBarry.Harding@Sun.COM 		 * R14. It would be preferable if MAC drivers don't
331210506SBarry.Harding@Sun.COM 		 * hold any locks across any mac call. However at a
331310506SBarry.Harding@Sun.COM 		 * minimum they must not hold any locks across data
331410506SBarry.Harding@Sun.COM 		 * upcalls. They must also make sure that all
331510506SBarry.Harding@Sun.COM 		 * references to mac data structures are cleaned up
331610506SBarry.Harding@Sun.COM 		 * and that it is single threaded at mac_unregister
331710506SBarry.Harding@Sun.COM 		 * time.
331810506SBarry.Harding@Sun.COM 		 */
331910506SBarry.Harding@Sun.COM 		err = mac_maxsdu_update(port->p_mh, new_mtu);
332010506SBarry.Harding@Sun.COM 		if (err != 0) {
332110506SBarry.Harding@Sun.COM 			/* This should never occur! */
332210506SBarry.Harding@Sun.COM 			yge_error(NULL, port,
332310506SBarry.Harding@Sun.COM 			    "Failed notifying GLDv3 of new maximum MTU");
332410506SBarry.Harding@Sun.COM 		} else {
332510506SBarry.Harding@Sun.COM 			port->p_mtu = new_mtu;
332610506SBarry.Harding@Sun.COM 		}
332710506SBarry.Harding@Sun.COM 		break;
332810506SBarry.Harding@Sun.COM 
332910506SBarry.Harding@Sun.COM 	default:
333010506SBarry.Harding@Sun.COM 		err = ENOTSUP;
333110506SBarry.Harding@Sun.COM 		break;
333210506SBarry.Harding@Sun.COM 	}
333310506SBarry.Harding@Sun.COM 
333410506SBarry.Harding@Sun.COM err:
333510506SBarry.Harding@Sun.COM 	DEV_UNLOCK(port->p_dev);
333610506SBarry.Harding@Sun.COM 
333710506SBarry.Harding@Sun.COM 	return (err);
333810506SBarry.Harding@Sun.COM }
333910506SBarry.Harding@Sun.COM 
334010506SBarry.Harding@Sun.COM int
yge_m_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)334110506SBarry.Harding@Sun.COM yge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
334211878SVenu.Iyer@Sun.COM     uint_t pr_valsize, void *pr_val)
334310506SBarry.Harding@Sun.COM {
334410506SBarry.Harding@Sun.COM 	yge_port_t	*port = arg;
334511878SVenu.Iyer@Sun.COM 
334611878SVenu.Iyer@Sun.COM 	return (mii_m_getprop(port->p_mii, pr_name, pr_num, pr_valsize,
334711878SVenu.Iyer@Sun.COM 	    pr_val));
334811878SVenu.Iyer@Sun.COM }
334911878SVenu.Iyer@Sun.COM 
335011878SVenu.Iyer@Sun.COM static void
yge_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)335111878SVenu.Iyer@Sun.COM yge_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
335211878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
335311878SVenu.Iyer@Sun.COM {
335411878SVenu.Iyer@Sun.COM 	yge_port_t	*port = arg;
335510506SBarry.Harding@Sun.COM 
335610506SBarry.Harding@Sun.COM 	switch (pr_num) {
335710506SBarry.Harding@Sun.COM 	case MAC_PROP_MTU:
335811878SVenu.Iyer@Sun.COM 		mac_prop_info_set_range_uint32(prh, ETHERMTU,
335910506SBarry.Harding@Sun.COM 		    port->p_flags & PORT_FLAG_NOJUMBO ?
336011878SVenu.Iyer@Sun.COM 		    ETHERMTU : YGE_JUMBO_MTU);
336110506SBarry.Harding@Sun.COM 		break;
336210506SBarry.Harding@Sun.COM 	default:
336311878SVenu.Iyer@Sun.COM 		mii_m_propinfo(port->p_mii, pr_name, pr_num, prh);
336410506SBarry.Harding@Sun.COM 		break;
336510506SBarry.Harding@Sun.COM 	}
336610506SBarry.Harding@Sun.COM }
336710506SBarry.Harding@Sun.COM 
336810506SBarry.Harding@Sun.COM void
yge_dispatch(yge_dev_t * dev,int flag)336910506SBarry.Harding@Sun.COM yge_dispatch(yge_dev_t *dev, int flag)
337010506SBarry.Harding@Sun.COM {
337110506SBarry.Harding@Sun.COM 	TASK_LOCK(dev);
337210506SBarry.Harding@Sun.COM 	dev->d_task_flags |= flag;
337310506SBarry.Harding@Sun.COM 	TASK_SIGNAL(dev);
337410506SBarry.Harding@Sun.COM 	TASK_UNLOCK(dev);
337510506SBarry.Harding@Sun.COM }
337610506SBarry.Harding@Sun.COM 
337710506SBarry.Harding@Sun.COM void
yge_task(void * arg)337810506SBarry.Harding@Sun.COM yge_task(void *arg)
337910506SBarry.Harding@Sun.COM {
338010506SBarry.Harding@Sun.COM 	yge_dev_t	*dev = arg;
338110506SBarry.Harding@Sun.COM 	int		flags;
338210506SBarry.Harding@Sun.COM 
338310506SBarry.Harding@Sun.COM 	for (;;) {
338410506SBarry.Harding@Sun.COM 
338510506SBarry.Harding@Sun.COM 		TASK_LOCK(dev);
338610506SBarry.Harding@Sun.COM 		while ((flags = dev->d_task_flags) == 0)
338710506SBarry.Harding@Sun.COM 			TASK_WAIT(dev);
338810506SBarry.Harding@Sun.COM 
338910506SBarry.Harding@Sun.COM 		dev->d_task_flags = 0;
339010506SBarry.Harding@Sun.COM 		TASK_UNLOCK(dev);
339110506SBarry.Harding@Sun.COM 
339210506SBarry.Harding@Sun.COM 		/*
339310506SBarry.Harding@Sun.COM 		 * This should be the first thing after the sleep so if we are
339410506SBarry.Harding@Sun.COM 		 * requested to exit we do that and not waste time doing work
339510506SBarry.Harding@Sun.COM 		 * we will then abandone.
339610506SBarry.Harding@Sun.COM 		 */
339710506SBarry.Harding@Sun.COM 		if (flags & YGE_TASK_EXIT)
339810506SBarry.Harding@Sun.COM 			break;
339910506SBarry.Harding@Sun.COM 
340010506SBarry.Harding@Sun.COM 		/* all processing done without holding locks */
340110506SBarry.Harding@Sun.COM 		if (flags & YGE_TASK_RESTART)
340210506SBarry.Harding@Sun.COM 			yge_restart_task(dev);
340310506SBarry.Harding@Sun.COM 	}
340410506SBarry.Harding@Sun.COM }
340510506SBarry.Harding@Sun.COM 
340610506SBarry.Harding@Sun.COM void
yge_error(yge_dev_t * dev,yge_port_t * port,char * fmt,...)340710506SBarry.Harding@Sun.COM yge_error(yge_dev_t *dev, yge_port_t *port, char *fmt, ...)
340810506SBarry.Harding@Sun.COM {
340910506SBarry.Harding@Sun.COM 	va_list		ap;
341010506SBarry.Harding@Sun.COM 	char		buf[256];
341111869SVitezslav.Batrla@Sun.COM 	int		ppa;
341210506SBarry.Harding@Sun.COM 
341310506SBarry.Harding@Sun.COM 	va_start(ap, fmt);
341410506SBarry.Harding@Sun.COM 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
341510506SBarry.Harding@Sun.COM 	va_end(ap);
341610506SBarry.Harding@Sun.COM 
341711869SVitezslav.Batrla@Sun.COM 	if (dev == NULL && port == NULL) {
341811869SVitezslav.Batrla@Sun.COM 		cmn_err(CE_WARN, "yge: %s", buf);
341911869SVitezslav.Batrla@Sun.COM 	} else {
342011869SVitezslav.Batrla@Sun.COM 		if (port != NULL)
342111869SVitezslav.Batrla@Sun.COM 			ppa = port->p_ppa;
342211869SVitezslav.Batrla@Sun.COM 		else
342311869SVitezslav.Batrla@Sun.COM 			ppa = ddi_get_instance(dev->d_dip);
342411869SVitezslav.Batrla@Sun.COM 		cmn_err(CE_WARN, "yge%d: %s", ppa, buf);
342511869SVitezslav.Batrla@Sun.COM 	}
342610506SBarry.Harding@Sun.COM }
342710506SBarry.Harding@Sun.COM 
342810506SBarry.Harding@Sun.COM static int
yge_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)342910506SBarry.Harding@Sun.COM yge_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
343010506SBarry.Harding@Sun.COM {
343110506SBarry.Harding@Sun.COM 	yge_dev_t	*dev;
343210506SBarry.Harding@Sun.COM 	int		rv;
343310506SBarry.Harding@Sun.COM 
343410506SBarry.Harding@Sun.COM 	switch (cmd) {
343510506SBarry.Harding@Sun.COM 	case DDI_ATTACH:
343610506SBarry.Harding@Sun.COM 		dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
343710506SBarry.Harding@Sun.COM 		dev->d_port[0] = kmem_zalloc(sizeof (yge_port_t), KM_SLEEP);
343810506SBarry.Harding@Sun.COM 		dev->d_port[1] = kmem_zalloc(sizeof (yge_port_t), KM_SLEEP);
343910506SBarry.Harding@Sun.COM 		dev->d_dip = dip;
344010506SBarry.Harding@Sun.COM 		ddi_set_driver_private(dip, dev);
344110506SBarry.Harding@Sun.COM 
344210506SBarry.Harding@Sun.COM 		dev->d_port[0]->p_port = 0;
344310506SBarry.Harding@Sun.COM 		dev->d_port[0]->p_dev = dev;
344410506SBarry.Harding@Sun.COM 		dev->d_port[1]->p_port = 0;
344510506SBarry.Harding@Sun.COM 		dev->d_port[1]->p_dev = dev;
344610506SBarry.Harding@Sun.COM 
344710506SBarry.Harding@Sun.COM 		rv = yge_attach(dev);
344810506SBarry.Harding@Sun.COM 		if (rv != DDI_SUCCESS) {
344910506SBarry.Harding@Sun.COM 			ddi_set_driver_private(dip, 0);
345010506SBarry.Harding@Sun.COM 			kmem_free(dev->d_port[1], sizeof (yge_port_t));
345110506SBarry.Harding@Sun.COM 			kmem_free(dev->d_port[0], sizeof (yge_port_t));
345210506SBarry.Harding@Sun.COM 			kmem_free(dev, sizeof (*dev));
345310506SBarry.Harding@Sun.COM 		}
345410506SBarry.Harding@Sun.COM 		return (rv);
345510506SBarry.Harding@Sun.COM 
345610506SBarry.Harding@Sun.COM 	case DDI_RESUME:
345710506SBarry.Harding@Sun.COM 		dev = ddi_get_driver_private(dip);
345810506SBarry.Harding@Sun.COM 		ASSERT(dev != NULL);
345910506SBarry.Harding@Sun.COM 		return (yge_resume(dev));
346010506SBarry.Harding@Sun.COM 
346110506SBarry.Harding@Sun.COM 	default:
346210506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
346310506SBarry.Harding@Sun.COM 	}
346410506SBarry.Harding@Sun.COM }
346510506SBarry.Harding@Sun.COM 
346610506SBarry.Harding@Sun.COM static int
yge_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)346710506SBarry.Harding@Sun.COM yge_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
346810506SBarry.Harding@Sun.COM {
346910506SBarry.Harding@Sun.COM 	yge_dev_t	*dev;
3470*12390Sgdamore@opensolaris.org 	mac_handle_t	mh;
347110506SBarry.Harding@Sun.COM 
347210506SBarry.Harding@Sun.COM 	switch (cmd) {
347310506SBarry.Harding@Sun.COM 	case DDI_DETACH:
347410506SBarry.Harding@Sun.COM 
347510506SBarry.Harding@Sun.COM 		dev = ddi_get_driver_private(dip);
347610506SBarry.Harding@Sun.COM 
347710506SBarry.Harding@Sun.COM 		/* attempt to unregister MACs from Nemo */
347810506SBarry.Harding@Sun.COM 		for (int i = 0; i < dev->d_num_port; i++) {
3479*12390Sgdamore@opensolaris.org 
3480*12390Sgdamore@opensolaris.org 			if (((mh = dev->d_port[i]->p_mh) != NULL) &&
3481*12390Sgdamore@opensolaris.org 			    (mac_disable(mh) != 0)) {
3482*12390Sgdamore@opensolaris.org 				/*
3483*12390Sgdamore@opensolaris.org 				 * We'd really like a mac_enable to reenable
3484*12390Sgdamore@opensolaris.org 				 * any MACs that we previously disabled.  Too
3485*12390Sgdamore@opensolaris.org 				 * bad GLDv3 doesn't have one.
3486*12390Sgdamore@opensolaris.org 				 */
348710506SBarry.Harding@Sun.COM 				return (DDI_FAILURE);
348810506SBarry.Harding@Sun.COM 			}
348910506SBarry.Harding@Sun.COM 		}
349010506SBarry.Harding@Sun.COM 
349110506SBarry.Harding@Sun.COM 		ASSERT(dip == dev->d_dip);
349210506SBarry.Harding@Sun.COM 		yge_detach(dev);
349310506SBarry.Harding@Sun.COM 		ddi_set_driver_private(dip, 0);
3494*12390Sgdamore@opensolaris.org 		for (int i = 0; i < dev->d_num_port; i++) {
3495*12390Sgdamore@opensolaris.org 			if ((mh = dev->d_port[i]->p_mh) != NULL) {
3496*12390Sgdamore@opensolaris.org 				/* This can't fail after mac_disable above. */
3497*12390Sgdamore@opensolaris.org 				(void) mac_unregister(mh);
3498*12390Sgdamore@opensolaris.org 			}
3499*12390Sgdamore@opensolaris.org 		}
350010506SBarry.Harding@Sun.COM 		kmem_free(dev->d_port[1], sizeof (yge_port_t));
350110506SBarry.Harding@Sun.COM 		kmem_free(dev->d_port[0], sizeof (yge_port_t));
350210506SBarry.Harding@Sun.COM 		kmem_free(dev, sizeof (*dev));
350310506SBarry.Harding@Sun.COM 		return (DDI_SUCCESS);
350410506SBarry.Harding@Sun.COM 
350510506SBarry.Harding@Sun.COM 	case DDI_SUSPEND:
350610506SBarry.Harding@Sun.COM 		dev = ddi_get_driver_private(dip);
350710506SBarry.Harding@Sun.COM 		ASSERT(dev != NULL);
350810506SBarry.Harding@Sun.COM 		return (yge_suspend(dev));
350910506SBarry.Harding@Sun.COM 
351010506SBarry.Harding@Sun.COM 	default:
351110506SBarry.Harding@Sun.COM 		return (DDI_FAILURE);
351210506SBarry.Harding@Sun.COM 	}
351310506SBarry.Harding@Sun.COM }
351410506SBarry.Harding@Sun.COM 
351510506SBarry.Harding@Sun.COM static int
yge_quiesce(dev_info_t * dip)351610506SBarry.Harding@Sun.COM yge_quiesce(dev_info_t *dip)
351710506SBarry.Harding@Sun.COM {
351810506SBarry.Harding@Sun.COM 	yge_dev_t *dev;
351910506SBarry.Harding@Sun.COM 
352010506SBarry.Harding@Sun.COM 	dev = ddi_get_driver_private(dip);
352110506SBarry.Harding@Sun.COM 	ASSERT(dev != NULL);
352210506SBarry.Harding@Sun.COM 
352310506SBarry.Harding@Sun.COM 	/* NB: No locking!  We are called in single threaded context */
352410506SBarry.Harding@Sun.COM 	for (int i = 0; i < dev->d_num_port; i++) {
352510506SBarry.Harding@Sun.COM 		yge_port_t *port = dev->d_port[i];
352610506SBarry.Harding@Sun.COM 		if (port->p_running)
352710506SBarry.Harding@Sun.COM 			yge_stop_port(port);
352810506SBarry.Harding@Sun.COM 	}
352910506SBarry.Harding@Sun.COM 
353010506SBarry.Harding@Sun.COM 	/* Disable all interrupts. */
353110506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_IMSK, 0);
353210506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_IMSK);
353310506SBarry.Harding@Sun.COM 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
353410506SBarry.Harding@Sun.COM 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
353510506SBarry.Harding@Sun.COM 
353610506SBarry.Harding@Sun.COM 	/* Put hardware into reset. */
353710506SBarry.Harding@Sun.COM 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
353810506SBarry.Harding@Sun.COM 
353910506SBarry.Harding@Sun.COM 	return (DDI_SUCCESS);
354010506SBarry.Harding@Sun.COM }
354110506SBarry.Harding@Sun.COM 
354210506SBarry.Harding@Sun.COM /*
354310506SBarry.Harding@Sun.COM  * Stream information
354410506SBarry.Harding@Sun.COM  */
354510506SBarry.Harding@Sun.COM DDI_DEFINE_STREAM_OPS(yge_devops, nulldev, nulldev, yge_ddi_attach,
354610506SBarry.Harding@Sun.COM     yge_ddi_detach, nodev, NULL, D_MP, NULL, yge_quiesce);
354710506SBarry.Harding@Sun.COM 
354810506SBarry.Harding@Sun.COM /*
354910506SBarry.Harding@Sun.COM  * Module linkage information.
355010506SBarry.Harding@Sun.COM  */
355110506SBarry.Harding@Sun.COM 
355210506SBarry.Harding@Sun.COM static struct modldrv yge_modldrv = {
355310506SBarry.Harding@Sun.COM 	&mod_driverops,			/* drv_modops */
355410506SBarry.Harding@Sun.COM 	"Yukon 2 Ethernet",		/* drv_linkinfo */
355510506SBarry.Harding@Sun.COM 	&yge_devops			/* drv_dev_ops */
355610506SBarry.Harding@Sun.COM };
355710506SBarry.Harding@Sun.COM 
355810506SBarry.Harding@Sun.COM static struct modlinkage yge_modlinkage = {
355910506SBarry.Harding@Sun.COM 	MODREV_1,		/* ml_rev */
356010506SBarry.Harding@Sun.COM 	&yge_modldrv,		/* ml_linkage */
356110506SBarry.Harding@Sun.COM 	NULL
356210506SBarry.Harding@Sun.COM };
356310506SBarry.Harding@Sun.COM 
356410506SBarry.Harding@Sun.COM /*
356510506SBarry.Harding@Sun.COM  * DDI entry points.
356610506SBarry.Harding@Sun.COM  */
356710506SBarry.Harding@Sun.COM int
_init(void)356810506SBarry.Harding@Sun.COM _init(void)
356910506SBarry.Harding@Sun.COM {
357010506SBarry.Harding@Sun.COM 	int	rv;
357110506SBarry.Harding@Sun.COM 	mac_init_ops(&yge_devops, "yge");
357210506SBarry.Harding@Sun.COM 	if ((rv = mod_install(&yge_modlinkage)) != DDI_SUCCESS) {
357310506SBarry.Harding@Sun.COM 		mac_fini_ops(&yge_devops);
357410506SBarry.Harding@Sun.COM 	}
357510506SBarry.Harding@Sun.COM 	return (rv);
357610506SBarry.Harding@Sun.COM }
357710506SBarry.Harding@Sun.COM 
357810506SBarry.Harding@Sun.COM int
_fini(void)357910506SBarry.Harding@Sun.COM _fini(void)
358010506SBarry.Harding@Sun.COM {
358110506SBarry.Harding@Sun.COM 	int	rv;
358210506SBarry.Harding@Sun.COM 	if ((rv = mod_remove(&yge_modlinkage)) == DDI_SUCCESS) {
358310506SBarry.Harding@Sun.COM 		mac_fini_ops(&yge_devops);
358410506SBarry.Harding@Sun.COM 	}
358510506SBarry.Harding@Sun.COM 	return (rv);
358610506SBarry.Harding@Sun.COM }
358710506SBarry.Harding@Sun.COM 
358810506SBarry.Harding@Sun.COM int
_info(struct modinfo * modinfop)358910506SBarry.Harding@Sun.COM _info(struct modinfo *modinfop)
359010506SBarry.Harding@Sun.COM {
359110506SBarry.Harding@Sun.COM 	return (mod_info(&yge_modlinkage, modinfop));
359210506SBarry.Harding@Sun.COM }
3593