xref: /titanic_51/usr/src/uts/common/io/yge/yge.c (revision 3661c01dca7430b985a4a5fc1ea8fa1764068940)
11816cb70SBarry Harding /*
2*3661c01dSGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
31816cb70SBarry Harding  */
41816cb70SBarry Harding 
51816cb70SBarry Harding /*
61816cb70SBarry Harding  * This driver was derived from the FreeBSD if_msk.c driver, which
71816cb70SBarry Harding  * bears the following copyright attributions and licenses.
81816cb70SBarry Harding  */
91816cb70SBarry Harding 
101816cb70SBarry Harding /*
111816cb70SBarry Harding  *
121816cb70SBarry Harding  *	LICENSE:
131816cb70SBarry Harding  *	Copyright (C) Marvell International Ltd. and/or its affiliates
141816cb70SBarry Harding  *
151816cb70SBarry Harding  *	The computer program files contained in this folder ("Files")
161816cb70SBarry Harding  *	are provided to you under the BSD-type license terms provided
171816cb70SBarry Harding  *	below, and any use of such Files and any derivative works
181816cb70SBarry Harding  *	thereof created by you shall be governed by the following terms
191816cb70SBarry Harding  *	and conditions:
201816cb70SBarry Harding  *
211816cb70SBarry Harding  *	- Redistributions of source code must retain the above copyright
221816cb70SBarry Harding  *	  notice, this list of conditions and the following disclaimer.
231816cb70SBarry Harding  *	- Redistributions in binary form must reproduce the above
241816cb70SBarry Harding  *	  copyright notice, this list of conditions and the following
251816cb70SBarry Harding  *	  disclaimer in the documentation and/or other materials provided
261816cb70SBarry Harding  *	  with the distribution.
271816cb70SBarry Harding  *	- Neither the name of Marvell nor the names of its contributors
281816cb70SBarry Harding  *	  may be used to endorse or promote products derived from this
291816cb70SBarry Harding  *	  software without specific prior written permission.
301816cb70SBarry Harding  *
311816cb70SBarry Harding  *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
321816cb70SBarry Harding  *	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
331816cb70SBarry Harding  *	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
341816cb70SBarry Harding  *	FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
351816cb70SBarry Harding  *	COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
361816cb70SBarry Harding  *	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
371816cb70SBarry Harding  *	BUT NOT LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR SERVICES;
381816cb70SBarry Harding  *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
391816cb70SBarry Harding  *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
401816cb70SBarry Harding  *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
411816cb70SBarry Harding  *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
421816cb70SBarry Harding  *	OF THE POSSIBILITY OF SUCH DAMAGE.
431816cb70SBarry Harding  *	/LICENSE
441816cb70SBarry Harding  *
451816cb70SBarry Harding  */
461816cb70SBarry Harding /*
471816cb70SBarry Harding  * Copyright (c) 1997, 1998, 1999, 2000
481816cb70SBarry Harding  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
491816cb70SBarry Harding  *
501816cb70SBarry Harding  * Redistribution and use in source and binary forms, with or without
511816cb70SBarry Harding  * modification, are permitted provided that the following conditions
521816cb70SBarry Harding  * are met:
531816cb70SBarry Harding  * 1. Redistributions of source code must retain the above copyright
541816cb70SBarry Harding  *    notice, this list of conditions and the following disclaimer.
551816cb70SBarry Harding  * 2. Redistributions in binary form must reproduce the above copyright
561816cb70SBarry Harding  *    notice, this list of conditions and the following disclaimer in the
571816cb70SBarry Harding  *    documentation and/or other materials provided with the distribution.
581816cb70SBarry Harding  * 3. All advertising materials mentioning features or use of this software
591816cb70SBarry Harding  *    must display the following acknowledgement:
601816cb70SBarry Harding  *	This product includes software developed by Bill Paul.
611816cb70SBarry Harding  * 4. Neither the name of the author nor the names of any co-contributors
621816cb70SBarry Harding  *    may be used to endorse or promote products derived from this software
631816cb70SBarry Harding  *    without specific prior written permission.
641816cb70SBarry Harding  *
651816cb70SBarry Harding  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
661816cb70SBarry Harding  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
671816cb70SBarry Harding  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
681816cb70SBarry Harding  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
691816cb70SBarry Harding  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
701816cb70SBarry Harding  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
711816cb70SBarry Harding  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
721816cb70SBarry Harding  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
731816cb70SBarry Harding  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
741816cb70SBarry Harding  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
751816cb70SBarry Harding  * THE POSSIBILITY OF SUCH DAMAGE.
761816cb70SBarry Harding  */
771816cb70SBarry Harding /*
781816cb70SBarry Harding  * Copyright (c) 2003 Nathan L. Binkert <binkertn@umich.edu>
791816cb70SBarry Harding  *
801816cb70SBarry Harding  * Permission to use, copy, modify, and distribute this software for any
811816cb70SBarry Harding  * purpose with or without fee is hereby granted, provided that the above
821816cb70SBarry Harding  * copyright notice and this permission notice appear in all copies.
831816cb70SBarry Harding  *
841816cb70SBarry Harding  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
851816cb70SBarry Harding  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
861816cb70SBarry Harding  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
871816cb70SBarry Harding  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
881816cb70SBarry Harding  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
891816cb70SBarry Harding  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
901816cb70SBarry Harding  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
911816cb70SBarry Harding  */
921816cb70SBarry Harding 
931816cb70SBarry Harding #include <sys/varargs.h>
941816cb70SBarry Harding #include <sys/types.h>
951816cb70SBarry Harding #include <sys/modctl.h>
961816cb70SBarry Harding #include <sys/conf.h>
971816cb70SBarry Harding #include <sys/devops.h>
981816cb70SBarry Harding #include <sys/stream.h>
991816cb70SBarry Harding #include <sys/strsun.h>
1001816cb70SBarry Harding #include <sys/cmn_err.h>
1011816cb70SBarry Harding #include <sys/ethernet.h>
1021816cb70SBarry Harding #include <sys/kmem.h>
1031816cb70SBarry Harding #include <sys/time.h>
1041816cb70SBarry Harding #include <sys/pci.h>
1051816cb70SBarry Harding #include <sys/mii.h>
1061816cb70SBarry Harding #include <sys/miiregs.h>
1071816cb70SBarry Harding #include <sys/mac.h>
1081816cb70SBarry Harding #include <sys/mac_ether.h>
1091816cb70SBarry Harding #include <sys/mac_provider.h>
1101816cb70SBarry Harding #include <sys/debug.h>
1111816cb70SBarry Harding #include <sys/note.h>
1121816cb70SBarry Harding #include <sys/ddi.h>
1131816cb70SBarry Harding #include <sys/sunddi.h>
1141816cb70SBarry Harding #include <sys/vlan.h>
1151816cb70SBarry Harding 
1161816cb70SBarry Harding #include "yge.h"
1171816cb70SBarry Harding 
1181816cb70SBarry Harding static struct ddi_device_acc_attr yge_regs_attr = {
1191816cb70SBarry Harding 	DDI_DEVICE_ATTR_V0,
1201816cb70SBarry Harding 	DDI_STRUCTURE_LE_ACC,
121837c1ac4SStephen Hanson 	DDI_STRICTORDER_ACC
1221816cb70SBarry Harding };
1231816cb70SBarry Harding 
1241816cb70SBarry Harding static struct ddi_device_acc_attr yge_ring_attr = {
1251816cb70SBarry Harding 	DDI_DEVICE_ATTR_V0,
1261816cb70SBarry Harding 	DDI_STRUCTURE_LE_ACC,
1271816cb70SBarry Harding 	DDI_STRICTORDER_ACC
1281816cb70SBarry Harding };
1291816cb70SBarry Harding 
1301816cb70SBarry Harding static struct ddi_device_acc_attr yge_buf_attr = {
1311816cb70SBarry Harding 	DDI_DEVICE_ATTR_V0,
1321816cb70SBarry Harding 	DDI_NEVERSWAP_ACC,
1331816cb70SBarry Harding 	DDI_STRICTORDER_ACC
1341816cb70SBarry Harding };
1351816cb70SBarry Harding 
1361816cb70SBarry Harding #define	DESC_ALIGN	0x1000
1371816cb70SBarry Harding 
1381816cb70SBarry Harding static ddi_dma_attr_t yge_ring_dma_attr = {
1391816cb70SBarry Harding 	DMA_ATTR_V0,		/* dma_attr_version */
1401816cb70SBarry Harding 	0,			/* dma_attr_addr_lo */
1411816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_addr_hi */
1421816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_count_max */
1431816cb70SBarry Harding 	DESC_ALIGN,		/* dma_attr_align */
1441816cb70SBarry Harding 	0x000007fc,		/* dma_attr_burstsizes */
1451816cb70SBarry Harding 	1,			/* dma_attr_minxfer */
1461816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_maxxfer */
1471816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_seg */
1481816cb70SBarry Harding 	1,			/* dma_attr_sgllen */
1491816cb70SBarry Harding 	1,			/* dma_attr_granular */
1501816cb70SBarry Harding 	0			/* dma_attr_flags */
1511816cb70SBarry Harding };
1521816cb70SBarry Harding 
1531816cb70SBarry Harding static ddi_dma_attr_t yge_buf_dma_attr = {
1541816cb70SBarry Harding 	DMA_ATTR_V0,		/* dma_attr_version */
1551816cb70SBarry Harding 	0,			/* dma_attr_addr_lo */
1561816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_addr_hi */
1571816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_count_max */
1581816cb70SBarry Harding 	1,			/* dma_attr_align */
1591816cb70SBarry Harding 	0x0000fffc,		/* dma_attr_burstsizes */
1601816cb70SBarry Harding 	1,			/* dma_attr_minxfer */
1611816cb70SBarry Harding 	0x000000000000ffffull,	/* dma_attr_maxxfer */
1621816cb70SBarry Harding 	0x00000000ffffffffull,	/* dma_attr_seg */
1631816cb70SBarry Harding 	8,			/* dma_attr_sgllen */
1641816cb70SBarry Harding 	1,			/* dma_attr_granular */
1651816cb70SBarry Harding 	0			/* dma_attr_flags */
1661816cb70SBarry Harding };
1671816cb70SBarry Harding 
1681816cb70SBarry Harding 
1691816cb70SBarry Harding static int yge_attach(yge_dev_t *);
1701816cb70SBarry Harding static void yge_detach(yge_dev_t *);
1711816cb70SBarry Harding static int yge_suspend(yge_dev_t *);
1721816cb70SBarry Harding static int yge_resume(yge_dev_t *);
1731816cb70SBarry Harding 
1741816cb70SBarry Harding static void yge_reset(yge_dev_t *);
1751816cb70SBarry Harding static void yge_setup_rambuffer(yge_dev_t *);
1761816cb70SBarry Harding 
1771816cb70SBarry Harding static int yge_init_port(yge_port_t *);
1781816cb70SBarry Harding static void yge_uninit_port(yge_port_t *);
1791816cb70SBarry Harding static int yge_register_port(yge_port_t *);
1801816cb70SBarry Harding 
1811816cb70SBarry Harding static void yge_tick(void *);
1821816cb70SBarry Harding static uint_t yge_intr(caddr_t, caddr_t);
1831816cb70SBarry Harding static int yge_intr_gmac(yge_port_t *);
1841816cb70SBarry Harding static void yge_intr_enable(yge_dev_t *);
1851816cb70SBarry Harding static void yge_intr_disable(yge_dev_t *);
1861816cb70SBarry Harding static boolean_t yge_handle_events(yge_dev_t *, mblk_t **, mblk_t **, int *);
1871816cb70SBarry Harding static void yge_handle_hwerr(yge_port_t *, uint32_t);
1881816cb70SBarry Harding static void yge_intr_hwerr(yge_dev_t *);
1891816cb70SBarry Harding static mblk_t *yge_rxeof(yge_port_t *, uint32_t, int);
1901816cb70SBarry Harding static void yge_txeof(yge_port_t *, int);
1911816cb70SBarry Harding static boolean_t yge_send(yge_port_t *, mblk_t *);
1921816cb70SBarry Harding static void yge_set_prefetch(yge_dev_t *, int, yge_ring_t *);
1931816cb70SBarry Harding static void yge_set_rambuffer(yge_port_t *);
1941816cb70SBarry Harding static void yge_start_port(yge_port_t *);
1951816cb70SBarry Harding static void yge_stop_port(yge_port_t *);
1961816cb70SBarry Harding static void yge_phy_power(yge_dev_t *, boolean_t);
1971816cb70SBarry Harding static int yge_alloc_ring(yge_port_t *, yge_dev_t *, yge_ring_t *, uint32_t);
1981816cb70SBarry Harding static void yge_free_ring(yge_ring_t *);
1991816cb70SBarry Harding static uint8_t yge_find_capability(yge_dev_t *, uint8_t);
2001816cb70SBarry Harding 
2011816cb70SBarry Harding static int yge_txrx_dma_alloc(yge_port_t *);
2021816cb70SBarry Harding static void yge_txrx_dma_free(yge_port_t *);
2031816cb70SBarry Harding static void yge_init_rx_ring(yge_port_t *);
2041816cb70SBarry Harding static void yge_init_tx_ring(yge_port_t *);
2051816cb70SBarry Harding 
2061816cb70SBarry Harding static uint16_t yge_mii_readreg(yge_port_t *, uint8_t, uint8_t);
2071816cb70SBarry Harding static void yge_mii_writereg(yge_port_t *, uint8_t, uint8_t, uint16_t);
2081816cb70SBarry Harding 
2091816cb70SBarry Harding static uint16_t yge_mii_read(void *, uint8_t, uint8_t);
2101816cb70SBarry Harding static void yge_mii_write(void *, uint8_t, uint8_t, uint16_t);
2111816cb70SBarry Harding static void yge_mii_notify(void *, link_state_t);
2121816cb70SBarry Harding 
2131816cb70SBarry Harding static void yge_setrxfilt(yge_port_t *);
2141816cb70SBarry Harding static void yge_restart_task(yge_dev_t *);
2151816cb70SBarry Harding static void yge_task(void *);
2161816cb70SBarry Harding static void yge_dispatch(yge_dev_t *, int);
2171816cb70SBarry Harding 
2181816cb70SBarry Harding static void yge_stats_clear(yge_port_t *);
2191816cb70SBarry Harding static void yge_stats_update(yge_port_t *);
2201816cb70SBarry Harding static uint32_t yge_hashbit(const uint8_t *);
2211816cb70SBarry Harding 
2221816cb70SBarry Harding static int yge_m_unicst(void *, const uint8_t *);
2231816cb70SBarry Harding static int yge_m_multicst(void *, boolean_t, const uint8_t *);
2241816cb70SBarry Harding static int yge_m_promisc(void *, boolean_t);
2251816cb70SBarry Harding static mblk_t *yge_m_tx(void *, mblk_t *);
2261816cb70SBarry Harding static int yge_m_stat(void *, uint_t, uint64_t *);
2271816cb70SBarry Harding static int yge_m_start(void *);
2281816cb70SBarry Harding static void yge_m_stop(void *);
2290dc2366fSVenugopal Iyer static int yge_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
2300dc2366fSVenugopal Iyer static void yge_m_propinfo(void *, const char *, mac_prop_id_t,
2310dc2366fSVenugopal Iyer     mac_prop_info_handle_t);
2321816cb70SBarry Harding static int yge_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
2331816cb70SBarry Harding     const void *);
2341816cb70SBarry Harding static void yge_m_ioctl(void *, queue_t *, mblk_t *);
2351816cb70SBarry Harding 
2361816cb70SBarry Harding void yge_error(yge_dev_t *, yge_port_t *, char *, ...);
2371816cb70SBarry Harding extern void yge_phys_update(yge_port_t *);
2381816cb70SBarry Harding extern int yge_phys_restart(yge_port_t *, boolean_t);
2391816cb70SBarry Harding extern int yge_phys_init(yge_port_t *, phy_readreg_t, phy_writereg_t);
2401816cb70SBarry Harding 
2411816cb70SBarry Harding static mac_callbacks_t yge_m_callbacks = {
2420dc2366fSVenugopal Iyer 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2431816cb70SBarry Harding 	yge_m_stat,
2441816cb70SBarry Harding 	yge_m_start,
2451816cb70SBarry Harding 	yge_m_stop,
2461816cb70SBarry Harding 	yge_m_promisc,
2471816cb70SBarry Harding 	yge_m_multicst,
2481816cb70SBarry Harding 	yge_m_unicst,
2491816cb70SBarry Harding 	yge_m_tx,
2500dc2366fSVenugopal Iyer 	NULL,
2511816cb70SBarry Harding 	yge_m_ioctl,
2521816cb70SBarry Harding 	NULL,		/* mc_getcapab */
2531816cb70SBarry Harding 	NULL,		/* mc_open */
2541816cb70SBarry Harding 	NULL,		/* mc_close */
2551816cb70SBarry Harding 	yge_m_setprop,
2561816cb70SBarry Harding 	yge_m_getprop,
2570dc2366fSVenugopal Iyer 	yge_m_propinfo
2581816cb70SBarry Harding };
2591816cb70SBarry Harding 
2601816cb70SBarry Harding static mii_ops_t yge_mii_ops = {
2611816cb70SBarry Harding 	MII_OPS_VERSION,
2621816cb70SBarry Harding 	yge_mii_read,
2631816cb70SBarry Harding 	yge_mii_write,
2641816cb70SBarry Harding 	yge_mii_notify,
2651816cb70SBarry Harding 	NULL	/* reset */
2661816cb70SBarry Harding };
2671816cb70SBarry Harding 
2681816cb70SBarry Harding /*
2691816cb70SBarry Harding  * This is the low level interface routine to read from the PHY
2701816cb70SBarry Harding  * MII registers. There is multiple steps to these accesses. First
2711816cb70SBarry Harding  * the register number is written to an address register. Then after
2721816cb70SBarry Harding  * a specified delay status is checked until the data is present.
2731816cb70SBarry Harding  */
2741816cb70SBarry Harding static uint16_t
2751816cb70SBarry Harding yge_mii_readreg(yge_port_t *port, uint8_t phy, uint8_t reg)
2761816cb70SBarry Harding {
2771816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
2781816cb70SBarry Harding 	int pnum = port->p_port;
2791816cb70SBarry Harding 	uint16_t val;
2801816cb70SBarry Harding 
2811816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_SMI_CTRL,
2821816cb70SBarry Harding 	    GM_SMI_CT_PHY_AD(phy) | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
2831816cb70SBarry Harding 
2841816cb70SBarry Harding 	for (int i = 0; i < YGE_TIMEOUT; i += 10) {
2851816cb70SBarry Harding 		drv_usecwait(10);
2861816cb70SBarry Harding 		val = GMAC_READ_2(dev, pnum, GM_SMI_CTRL);
2871816cb70SBarry Harding 		if ((val & GM_SMI_CT_RD_VAL) != 0) {
2881816cb70SBarry Harding 			val = GMAC_READ_2(dev, pnum, GM_SMI_DATA);
2891816cb70SBarry Harding 			return (val);
2901816cb70SBarry Harding 		}
2911816cb70SBarry Harding 	}
2921816cb70SBarry Harding 
2931816cb70SBarry Harding 	return (0xffff);
2941816cb70SBarry Harding }
2951816cb70SBarry Harding 
2961816cb70SBarry Harding /*
2971816cb70SBarry Harding  * This is the low level interface routine to write to the PHY
2981816cb70SBarry Harding  * MII registers. There is multiple steps to these accesses. The
2991816cb70SBarry Harding  * data and the target registers address are written to the PHY.
3001816cb70SBarry Harding  * Then the PHY is polled until it is done with the write. Note
3011816cb70SBarry Harding  * that the delays are specified and required!
3021816cb70SBarry Harding  */
3031816cb70SBarry Harding static void
3041816cb70SBarry Harding yge_mii_writereg(yge_port_t *port, uint8_t phy, uint8_t reg, uint16_t val)
3051816cb70SBarry Harding {
3061816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
3071816cb70SBarry Harding 	int pnum = port->p_port;
3081816cb70SBarry Harding 
3091816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_SMI_DATA, val);
3101816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_SMI_CTRL,
3111816cb70SBarry Harding 	    GM_SMI_CT_PHY_AD(phy) | GM_SMI_CT_REG_AD(reg));
3121816cb70SBarry Harding 
3131816cb70SBarry Harding 	for (int i = 0; i < YGE_TIMEOUT; i += 10) {
3141816cb70SBarry Harding 		drv_usecwait(10);
3151816cb70SBarry Harding 		if ((GMAC_READ_2(dev, pnum, GM_SMI_CTRL) & GM_SMI_CT_BUSY) == 0)
3161816cb70SBarry Harding 			return;
3171816cb70SBarry Harding 	}
3181816cb70SBarry Harding 
3191816cb70SBarry Harding 	yge_error(NULL, port, "phy write timeout");
3201816cb70SBarry Harding }
3211816cb70SBarry Harding 
3221816cb70SBarry Harding static uint16_t
3231816cb70SBarry Harding yge_mii_read(void *arg, uint8_t phy, uint8_t reg)
3241816cb70SBarry Harding {
3251816cb70SBarry Harding 	yge_port_t *port = arg;
3261816cb70SBarry Harding 	uint16_t rv;
3271816cb70SBarry Harding 
3281816cb70SBarry Harding 	PHY_LOCK(port->p_dev);
3291816cb70SBarry Harding 	rv = yge_mii_readreg(port, phy, reg);
3301816cb70SBarry Harding 	PHY_UNLOCK(port->p_dev);
3311816cb70SBarry Harding 	return (rv);
3321816cb70SBarry Harding }
3331816cb70SBarry Harding 
3341816cb70SBarry Harding static void
3351816cb70SBarry Harding yge_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
3361816cb70SBarry Harding {
3371816cb70SBarry Harding 	yge_port_t *port = arg;
3381816cb70SBarry Harding 
3391816cb70SBarry Harding 	PHY_LOCK(port->p_dev);
3401816cb70SBarry Harding 	yge_mii_writereg(port, phy, reg, val);
3411816cb70SBarry Harding 	PHY_UNLOCK(port->p_dev);
3421816cb70SBarry Harding }
3431816cb70SBarry Harding 
3441816cb70SBarry Harding /*
3451816cb70SBarry Harding  * The MII common code calls this function to let the MAC driver
3461816cb70SBarry Harding  * know when there has been a change in status.
3471816cb70SBarry Harding  */
3481816cb70SBarry Harding void
3491816cb70SBarry Harding yge_mii_notify(void *arg, link_state_t link)
3501816cb70SBarry Harding {
3511816cb70SBarry Harding 	yge_port_t *port = arg;
3521816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
3531816cb70SBarry Harding 	uint32_t gmac;
3541816cb70SBarry Harding 	uint32_t gpcr;
3551816cb70SBarry Harding 	link_flowctrl_t	fc;
3561816cb70SBarry Harding 	link_duplex_t duplex;
3571816cb70SBarry Harding 	int speed;
3581816cb70SBarry Harding 
3591816cb70SBarry Harding 	fc = mii_get_flowctrl(port->p_mii);
3601816cb70SBarry Harding 	duplex = mii_get_duplex(port->p_mii);
3611816cb70SBarry Harding 	speed = mii_get_speed(port->p_mii);
3621816cb70SBarry Harding 
3631816cb70SBarry Harding 	DEV_LOCK(dev);
3641816cb70SBarry Harding 
3651816cb70SBarry Harding 	if (link == LINK_STATE_UP) {
3661816cb70SBarry Harding 
3671816cb70SBarry Harding 		/* Enable Tx FIFO Underrun. */
3681816cb70SBarry Harding 		CSR_WRITE_1(dev, MR_ADDR(port->p_port, GMAC_IRQ_MSK),
3691816cb70SBarry Harding 		    GM_IS_TX_FF_UR |	/* TX FIFO underflow */
3701816cb70SBarry Harding 		    GM_IS_RX_FF_OR);	/* RX FIFO overflow */
3711816cb70SBarry Harding 
3721816cb70SBarry Harding 		gpcr = GM_GPCR_AU_ALL_DIS;
3731816cb70SBarry Harding 
3741816cb70SBarry Harding 		switch (fc) {
3751816cb70SBarry Harding 		case LINK_FLOWCTRL_BI:
3761816cb70SBarry Harding 			gmac = GMC_PAUSE_ON;
3771816cb70SBarry Harding 			gpcr &= ~(GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS);
3781816cb70SBarry Harding 			break;
3791816cb70SBarry Harding 		case LINK_FLOWCTRL_TX:
3801816cb70SBarry Harding 			gmac = GMC_PAUSE_ON;
3811816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_RX_DIS;
3821816cb70SBarry Harding 			break;
3831816cb70SBarry Harding 		case LINK_FLOWCTRL_RX:
3841816cb70SBarry Harding 			gmac = GMC_PAUSE_ON;
3851816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_TX_DIS;
3861816cb70SBarry Harding 			break;
3871816cb70SBarry Harding 		case LINK_FLOWCTRL_NONE:
3881816cb70SBarry Harding 		default:
3891816cb70SBarry Harding 			gmac = GMC_PAUSE_OFF;
3901816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_RX_DIS;
3911816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_TX_DIS;
3921816cb70SBarry Harding 			break;
3931816cb70SBarry Harding 		}
3941816cb70SBarry Harding 
3951816cb70SBarry Harding 		gpcr &= ~((GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100));
3961816cb70SBarry Harding 		switch (speed) {
3971816cb70SBarry Harding 		case 1000:
3981816cb70SBarry Harding 			gpcr |= GM_GPCR_SPEED_1000;
3991816cb70SBarry Harding 			break;
4001816cb70SBarry Harding 		case 100:
4011816cb70SBarry Harding 			gpcr |= GM_GPCR_SPEED_100;
4021816cb70SBarry Harding 			break;
4031816cb70SBarry Harding 		case 10:
4041816cb70SBarry Harding 		default:
4051816cb70SBarry Harding 			break;
4061816cb70SBarry Harding 		}
4071816cb70SBarry Harding 
4081816cb70SBarry Harding 		if (duplex == LINK_DUPLEX_FULL) {
4091816cb70SBarry Harding 			gpcr |= GM_GPCR_DUP_FULL;
4101816cb70SBarry Harding 		} else {
4111816cb70SBarry Harding 			gpcr &= ~(GM_GPCR_DUP_FULL);
4121816cb70SBarry Harding 			gmac = GMC_PAUSE_OFF;
4131816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_RX_DIS;
4141816cb70SBarry Harding 			gpcr |= GM_GPCR_FC_TX_DIS;
4151816cb70SBarry Harding 		}
4161816cb70SBarry Harding 
4171816cb70SBarry Harding 		gpcr |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
4181816cb70SBarry Harding 		GMAC_WRITE_2(dev, port->p_port, GM_GP_CTRL, gpcr);
4191816cb70SBarry Harding 
4201816cb70SBarry Harding 		/* Read again to ensure writing. */
4211816cb70SBarry Harding 		(void) GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
4221816cb70SBarry Harding 
4231816cb70SBarry Harding 		/* write out the flow control gmac setting */
4241816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(port->p_port, GMAC_CTRL), gmac);
4251816cb70SBarry Harding 
4261816cb70SBarry Harding 	} else {
4271816cb70SBarry Harding 		/* Disable Rx/Tx MAC. */
4281816cb70SBarry Harding 		gpcr = GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
4291816cb70SBarry Harding 		gpcr &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
4301816cb70SBarry Harding 		GMAC_WRITE_2(dev, port->p_port, GM_GP_CTRL, gpcr);
4311816cb70SBarry Harding 
4321816cb70SBarry Harding 		/* Read again to ensure writing. */
4331816cb70SBarry Harding 		(void) GMAC_READ_2(dev, port->p_port, GM_GP_CTRL);
4341816cb70SBarry Harding 	}
4351816cb70SBarry Harding 
4361816cb70SBarry Harding 	DEV_UNLOCK(dev);
4371816cb70SBarry Harding 
4381816cb70SBarry Harding 	mac_link_update(port->p_mh, link);
4391816cb70SBarry Harding 
4401816cb70SBarry Harding 	if (port->p_running && (link == LINK_STATE_UP)) {
4411816cb70SBarry Harding 		mac_tx_update(port->p_mh);
4421816cb70SBarry Harding 	}
4431816cb70SBarry Harding }
4441816cb70SBarry Harding 
4451816cb70SBarry Harding static void
4461816cb70SBarry Harding yge_setrxfilt(yge_port_t *port)
4471816cb70SBarry Harding {
4481816cb70SBarry Harding 	yge_dev_t	*dev;
4491816cb70SBarry Harding 	uint16_t	mode;
4501816cb70SBarry Harding 	uint8_t		*ea;
4511816cb70SBarry Harding 	uint32_t	*mchash;
4521816cb70SBarry Harding 	int		pnum;
4531816cb70SBarry Harding 
4541816cb70SBarry Harding 	dev = port->p_dev;
4551816cb70SBarry Harding 	pnum = port->p_port;
4561816cb70SBarry Harding 	ea = port->p_curraddr;
4571816cb70SBarry Harding 	mchash = port->p_mchash;
4581816cb70SBarry Harding 
4591816cb70SBarry Harding 	if (dev->d_suspended)
4601816cb70SBarry Harding 		return;
4611816cb70SBarry Harding 
4621816cb70SBarry Harding 	/* Set station address. */
4631816cb70SBarry Harding 	for (int i = 0; i < (ETHERADDRL / 2); i++) {
4641816cb70SBarry Harding 		GMAC_WRITE_2(dev, pnum, GM_SRC_ADDR_1L + i * 4,
4651816cb70SBarry Harding 		    ((uint16_t)ea[i * 2] | ((uint16_t)ea[(i * 2) + 1] << 8)));
4661816cb70SBarry Harding 	}
4671816cb70SBarry Harding 	for (int i = 0; i < (ETHERADDRL / 2); i++) {
4681816cb70SBarry Harding 		GMAC_WRITE_2(dev, pnum, GM_SRC_ADDR_2L + i * 4,
4691816cb70SBarry Harding 		    ((uint16_t)ea[i * 2] | ((uint16_t)ea[(i * 2) + 1] << 8)));
4701816cb70SBarry Harding 	}
4711816cb70SBarry Harding 
4721816cb70SBarry Harding 	/* Figure out receive filtering mode. */
4731816cb70SBarry Harding 	mode = GMAC_READ_2(dev, pnum, GM_RX_CTRL);
4741816cb70SBarry Harding 	if (port->p_promisc) {
4751816cb70SBarry Harding 		mode &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
4761816cb70SBarry Harding 	} else {
4771816cb70SBarry Harding 		mode |= (GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
4781816cb70SBarry Harding 	}
4791816cb70SBarry Harding 	/* Write the multicast filter. */
4801816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H1, mchash[0] & 0xffff);
4811816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H2, (mchash[0] >> 16) & 0xffff);
4821816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H3, mchash[1] & 0xffff);
4831816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_MC_ADDR_H4, (mchash[1] >> 16) & 0xffff);
4841816cb70SBarry Harding 	/* Write the receive filtering mode. */
4851816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_RX_CTRL, mode);
4861816cb70SBarry Harding }
4871816cb70SBarry Harding 
4881816cb70SBarry Harding static void
4891816cb70SBarry Harding yge_init_rx_ring(yge_port_t *port)
4901816cb70SBarry Harding {
4911816cb70SBarry Harding 	yge_buf_t *rxb;
4921816cb70SBarry Harding 	yge_ring_t *ring;
4931816cb70SBarry Harding 	int prod;
4941816cb70SBarry Harding 
4951816cb70SBarry Harding 	port->p_rx_cons = 0;
4961816cb70SBarry Harding 	port->p_rx_putwm = YGE_PUT_WM;
4971816cb70SBarry Harding 	ring = &port->p_rx_ring;
4981816cb70SBarry Harding 
4991816cb70SBarry Harding 	/* ala bzero, but uses safer acch access */
5001816cb70SBarry Harding 	CLEARRING(ring);
5011816cb70SBarry Harding 
5021816cb70SBarry Harding 	for (prod = 0; prod < YGE_RX_RING_CNT; prod++) {
5031816cb70SBarry Harding 		/* Hang out receive buffers. */
5041816cb70SBarry Harding 		rxb = &port->p_rx_buf[prod];
5051816cb70SBarry Harding 
5061816cb70SBarry Harding 		PUTADDR(ring, prod, rxb->b_paddr);
5071816cb70SBarry Harding 		PUTCTRL(ring, prod, port->p_framesize | OP_PACKET | HW_OWNER);
5081816cb70SBarry Harding 	}
5091816cb70SBarry Harding 
5101816cb70SBarry Harding 	SYNCRING(ring, DDI_DMA_SYNC_FORDEV);
5111816cb70SBarry Harding 
5121816cb70SBarry Harding 	yge_set_prefetch(port->p_dev, port->p_rxq, ring);
5131816cb70SBarry Harding 
5141816cb70SBarry Harding 	/* Update prefetch unit. */
5151816cb70SBarry Harding 	CSR_WRITE_2(port->p_dev,
5161816cb70SBarry Harding 	    Y2_PREF_Q_ADDR(port->p_rxq, PREF_UNIT_PUT_IDX_REG),
5171816cb70SBarry Harding 	    YGE_RX_RING_CNT - 1);
5181816cb70SBarry Harding }
5191816cb70SBarry Harding 
5201816cb70SBarry Harding static void
5211816cb70SBarry Harding yge_init_tx_ring(yge_port_t *port)
5221816cb70SBarry Harding {
5231816cb70SBarry Harding 	yge_ring_t *ring = &port->p_tx_ring;
5241816cb70SBarry Harding 
5251816cb70SBarry Harding 	port->p_tx_prod = 0;
5261816cb70SBarry Harding 	port->p_tx_cons = 0;
5271816cb70SBarry Harding 	port->p_tx_cnt = 0;
5281816cb70SBarry Harding 
5291816cb70SBarry Harding 	CLEARRING(ring);
5301816cb70SBarry Harding 	SYNCRING(ring, DDI_DMA_SYNC_FORDEV);
5311816cb70SBarry Harding 
5321816cb70SBarry Harding 	yge_set_prefetch(port->p_dev, port->p_txq, ring);
5331816cb70SBarry Harding }
5341816cb70SBarry Harding 
5351816cb70SBarry Harding static void
5361816cb70SBarry Harding yge_setup_rambuffer(yge_dev_t *dev)
5371816cb70SBarry Harding {
5381816cb70SBarry Harding 	int next;
5391816cb70SBarry Harding 	int i;
5401816cb70SBarry Harding 
5411816cb70SBarry Harding 	/* Get adapter SRAM size. */
5421816cb70SBarry Harding 	dev->d_ramsize = CSR_READ_1(dev, B2_E_0) * 4;
5431816cb70SBarry Harding 	if (dev->d_ramsize == 0)
5441816cb70SBarry Harding 		return;
5451816cb70SBarry Harding 
5461816cb70SBarry Harding 	dev->d_pflags |= PORT_FLAG_RAMBUF;
5471816cb70SBarry Harding 	/*
5481816cb70SBarry Harding 	 * Give receiver 2/3 of memory and round down to the multiple
5491816cb70SBarry Harding 	 * of 1024. Tx/Rx RAM buffer size of Yukon 2 should be multiple
5501816cb70SBarry Harding 	 * of 1024.
5511816cb70SBarry Harding 	 */
5521816cb70SBarry Harding 	dev->d_rxqsize = (((dev->d_ramsize * 1024 * 2) / 3) & ~(1024 - 1));
5531816cb70SBarry Harding 	dev->d_txqsize = (dev->d_ramsize * 1024) - dev->d_rxqsize;
5541816cb70SBarry Harding 
5551816cb70SBarry Harding 	for (i = 0, next = 0; i < dev->d_num_port; i++) {
5561816cb70SBarry Harding 		dev->d_rxqstart[i] = next;
5571816cb70SBarry Harding 		dev->d_rxqend[i] = next + dev->d_rxqsize - 1;
5581816cb70SBarry Harding 		next = dev->d_rxqend[i] + 1;
5591816cb70SBarry Harding 		dev->d_txqstart[i] = next;
5601816cb70SBarry Harding 		dev->d_txqend[i] = next + dev->d_txqsize - 1;
5611816cb70SBarry Harding 		next = dev->d_txqend[i] + 1;
5621816cb70SBarry Harding 	}
5631816cb70SBarry Harding }
5641816cb70SBarry Harding 
5651816cb70SBarry Harding static void
5661816cb70SBarry Harding yge_phy_power(yge_dev_t *dev, boolean_t powerup)
5671816cb70SBarry Harding {
5681816cb70SBarry Harding 	uint32_t val;
5691816cb70SBarry Harding 	int i;
5701816cb70SBarry Harding 
5711816cb70SBarry Harding 	if (powerup) {
5721816cb70SBarry Harding 		/* Switch power to VCC (WA for VAUX problem). */
5731816cb70SBarry Harding 		CSR_WRITE_1(dev, B0_POWER_CTRL,
5741816cb70SBarry Harding 		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
5751816cb70SBarry Harding 		/* Disable Core Clock Division, set Clock Select to 0. */
5761816cb70SBarry Harding 		CSR_WRITE_4(dev, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS);
5771816cb70SBarry Harding 
5781816cb70SBarry Harding 		val = 0;
5791816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
5801816cb70SBarry Harding 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
5811816cb70SBarry Harding 			/* Enable bits are inverted. */
5821816cb70SBarry Harding 			val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
5831816cb70SBarry Harding 			    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
5841816cb70SBarry Harding 			    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS;
5851816cb70SBarry Harding 		}
5861816cb70SBarry Harding 		/*
5871816cb70SBarry Harding 		 * Enable PCI & Core Clock, enable clock gating for both Links.
5881816cb70SBarry Harding 		 */
5891816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_Y2_CLK_GATE, val);
5901816cb70SBarry Harding 
5911816cb70SBarry Harding 		val = pci_config_get32(dev->d_pcih, PCI_OUR_REG_1);
5921816cb70SBarry Harding 		val &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
5931816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
5941816cb70SBarry Harding 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
5951816cb70SBarry Harding 			/* Deassert Low Power for 1st PHY. */
5961816cb70SBarry Harding 			val |= PCI_Y2_PHY1_COMA;
5971816cb70SBarry Harding 			if (dev->d_num_port > 1)
5981816cb70SBarry Harding 				val |= PCI_Y2_PHY2_COMA;
5991816cb70SBarry Harding 		}
6001816cb70SBarry Harding 
6011816cb70SBarry Harding 		/* Release PHY from PowerDown/COMA mode. */
6021816cb70SBarry Harding 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, val);
6031816cb70SBarry Harding 
6041816cb70SBarry Harding 		switch (dev->d_hw_id) {
6051816cb70SBarry Harding 		case CHIP_ID_YUKON_EC_U:
6061816cb70SBarry Harding 		case CHIP_ID_YUKON_EX:
6071816cb70SBarry Harding 		case CHIP_ID_YUKON_FE_P: {
6081816cb70SBarry Harding 			uint32_t our;
6091816cb70SBarry Harding 
6101816cb70SBarry Harding 			CSR_WRITE_2(dev, B0_CTST, Y2_HW_WOL_OFF);
6111816cb70SBarry Harding 
6121816cb70SBarry Harding 			/* Enable all clocks. */
6131816cb70SBarry Harding 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
6141816cb70SBarry Harding 
6151816cb70SBarry Harding 			our = pci_config_get32(dev->d_pcih, PCI_OUR_REG_4);
6161816cb70SBarry Harding 			our &= (PCI_FORCE_ASPM_REQUEST|PCI_ASPM_GPHY_LINK_DOWN|
6171816cb70SBarry Harding 			    PCI_ASPM_INT_FIFO_EMPTY|PCI_ASPM_CLKRUN_REQUEST);
6181816cb70SBarry Harding 			/* Set all bits to 0 except bits 15..12. */
6191816cb70SBarry Harding 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_4, our);
6201816cb70SBarry Harding 
6211816cb70SBarry Harding 			/* Set to default value. */
6221816cb70SBarry Harding 			our = pci_config_get32(dev->d_pcih, PCI_OUR_REG_5);
6231816cb70SBarry Harding 			our &= P_CTL_TIM_VMAIN_AV_MSK;
6241816cb70SBarry Harding 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_5, our);
6251816cb70SBarry Harding 
6261816cb70SBarry Harding 			pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, 0);
6271816cb70SBarry Harding 
6281816cb70SBarry Harding 			/*
6291816cb70SBarry Harding 			 * Enable workaround for dev 4.107 on Yukon-Ultra
6301816cb70SBarry Harding 			 * and Extreme
6311816cb70SBarry Harding 			 */
6321816cb70SBarry Harding 			our = CSR_READ_4(dev, B2_GP_IO);
6331816cb70SBarry Harding 			our |= GLB_GPIO_STAT_RACE_DIS;
6341816cb70SBarry Harding 			CSR_WRITE_4(dev, B2_GP_IO, our);
6351816cb70SBarry Harding 
6361816cb70SBarry Harding 			(void) CSR_READ_4(dev, B2_GP_IO);
6371816cb70SBarry Harding 			break;
6381816cb70SBarry Harding 		}
6391816cb70SBarry Harding 		default:
6401816cb70SBarry Harding 			break;
6411816cb70SBarry Harding 		}
6421816cb70SBarry Harding 
6431816cb70SBarry Harding 		for (i = 0; i < dev->d_num_port; i++) {
6441816cb70SBarry Harding 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_LINK_CTRL),
6451816cb70SBarry Harding 			    GMLC_RST_SET);
6461816cb70SBarry Harding 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_LINK_CTRL),
6471816cb70SBarry Harding 			    GMLC_RST_CLR);
6481816cb70SBarry Harding 		}
6491816cb70SBarry Harding 	} else {
6501816cb70SBarry Harding 		val = pci_config_get32(dev->d_pcih, PCI_OUR_REG_1);
6511816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
6521816cb70SBarry Harding 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
6531816cb70SBarry Harding 			val &= ~PCI_Y2_PHY1_COMA;
6541816cb70SBarry Harding 			if (dev->d_num_port > 1)
6551816cb70SBarry Harding 				val &= ~PCI_Y2_PHY2_COMA;
6561816cb70SBarry Harding 			val &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
6571816cb70SBarry Harding 		} else {
6581816cb70SBarry Harding 			val |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
6591816cb70SBarry Harding 		}
6601816cb70SBarry Harding 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_1, val);
6611816cb70SBarry Harding 
6621816cb70SBarry Harding 		val = Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
6631816cb70SBarry Harding 		    Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
6641816cb70SBarry Harding 		    Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS;
6651816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
6661816cb70SBarry Harding 		    dev->d_hw_rev > CHIP_REV_YU_XL_A1) {
6671816cb70SBarry Harding 			/* Enable bits are inverted. */
6681816cb70SBarry Harding 			val = 0;
6691816cb70SBarry Harding 		}
6701816cb70SBarry Harding 		/*
6711816cb70SBarry Harding 		 * Disable PCI & Core Clock, disable clock gating for
6721816cb70SBarry Harding 		 * both Links.
6731816cb70SBarry Harding 		 */
6741816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_Y2_CLK_GATE, val);
6751816cb70SBarry Harding 		CSR_WRITE_1(dev, B0_POWER_CTRL,
6761816cb70SBarry Harding 		    PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF);
6771816cb70SBarry Harding 	}
6781816cb70SBarry Harding }
6791816cb70SBarry Harding 
6801816cb70SBarry Harding static void
6811816cb70SBarry Harding yge_reset(yge_dev_t *dev)
6821816cb70SBarry Harding {
6831816cb70SBarry Harding 	uint64_t addr;
6841816cb70SBarry Harding 	uint16_t status;
6851816cb70SBarry Harding 	uint32_t val;
6861816cb70SBarry Harding 	int i;
6871816cb70SBarry Harding 	ddi_acc_handle_t	pcih = dev->d_pcih;
6881816cb70SBarry Harding 
6891816cb70SBarry Harding 	/* Turn off ASF */
6901816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_EX) {
6911816cb70SBarry Harding 		status = CSR_READ_2(dev, B28_Y2_ASF_STAT_CMD);
6921816cb70SBarry Harding 		/* Clear AHB bridge & microcontroller reset */
6931816cb70SBarry Harding 		status &= ~Y2_ASF_CPU_MODE;
6941816cb70SBarry Harding 		status &= ~Y2_ASF_AHB_RST;
6951816cb70SBarry Harding 		/* Clear ASF microcontroller state */
6961816cb70SBarry Harding 		status &= ~Y2_ASF_STAT_MSK;
6971816cb70SBarry Harding 		CSR_WRITE_2(dev, B28_Y2_ASF_STAT_CMD, status);
6981816cb70SBarry Harding 	} else {
6991816cb70SBarry Harding 		CSR_WRITE_1(dev, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
7001816cb70SBarry Harding 	}
7011816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, Y2_ASF_DISABLE);
7021816cb70SBarry Harding 
7031816cb70SBarry Harding 	/*
7041816cb70SBarry Harding 	 * Since we disabled ASF, S/W reset is required for Power Management.
7051816cb70SBarry Harding 	 */
7061816cb70SBarry Harding 	CSR_WRITE_1(dev, B0_CTST, CS_RST_SET);
7071816cb70SBarry Harding 	CSR_WRITE_1(dev, B0_CTST, CS_RST_CLR);
7081816cb70SBarry Harding 
7091816cb70SBarry Harding 	/* Allow writes to PCI config space */
7101816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
7111816cb70SBarry Harding 
7121816cb70SBarry Harding 	/* Clear all error bits in the PCI status register. */
7131816cb70SBarry Harding 	status = pci_config_get16(pcih, PCI_CONF_STAT);
7141816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
7151816cb70SBarry Harding 
7161816cb70SBarry Harding 	status |= (PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB |
7171816cb70SBarry Harding 	    PCI_STAT_R_TARG_AB | PCI_STAT_PERROR);
7181816cb70SBarry Harding 	pci_config_put16(pcih, PCI_CONF_STAT, status);
7191816cb70SBarry Harding 
7201816cb70SBarry Harding 	CSR_WRITE_1(dev, B0_CTST, CS_MRST_CLR);
7211816cb70SBarry Harding 
7221816cb70SBarry Harding 	switch (dev->d_bustype) {
7231816cb70SBarry Harding 	case PEX_BUS:
7241816cb70SBarry Harding 		/* Clear all PEX errors. */
7251816cb70SBarry Harding 		CSR_PCI_WRITE_4(dev, Y2_CFG_AER + AER_UNCOR_ERR, 0xffffffff);
7261816cb70SBarry Harding 
7271816cb70SBarry Harding 		/* is error bit status stuck? */
7281816cb70SBarry Harding 		val = CSR_PCI_READ_4(dev, PEX_UNC_ERR_STAT);
7291816cb70SBarry Harding 		if ((val & PEX_RX_OV) != 0) {
7301816cb70SBarry Harding 			dev->d_intrmask &= ~Y2_IS_HW_ERR;
7311816cb70SBarry Harding 			dev->d_intrhwemask &= ~Y2_IS_PCI_EXP;
7321816cb70SBarry Harding 		}
7331816cb70SBarry Harding 		break;
7341816cb70SBarry Harding 	case PCI_BUS:
7351816cb70SBarry Harding 		/* Set Cache Line Size to 2 (8 bytes) if configured to 0. */
7361816cb70SBarry Harding 		if (pci_config_get8(pcih, PCI_CONF_CACHE_LINESZ) == 0)
7371816cb70SBarry Harding 			pci_config_put16(pcih, PCI_CONF_CACHE_LINESZ, 2);
7381816cb70SBarry Harding 		break;
7391816cb70SBarry Harding 	case PCIX_BUS:
7401816cb70SBarry Harding 		/* Set Cache Line Size to 2 (8 bytes) if configured to 0. */
7411816cb70SBarry Harding 		if (pci_config_get8(pcih, PCI_CONF_CACHE_LINESZ) == 0)
7421816cb70SBarry Harding 			pci_config_put16(pcih, PCI_CONF_CACHE_LINESZ, 2);
7431816cb70SBarry Harding 
7441816cb70SBarry Harding 		/* Set Cache Line Size opt. */
7451816cb70SBarry Harding 		val = pci_config_get32(pcih, PCI_OUR_REG_1);
7461816cb70SBarry Harding 		val |= PCI_CLS_OPT;
7471816cb70SBarry Harding 		pci_config_put32(pcih, PCI_OUR_REG_1, val);
7481816cb70SBarry Harding 		break;
7491816cb70SBarry Harding 	}
7501816cb70SBarry Harding 
7511816cb70SBarry Harding 	/* Set PHY power state. */
7521816cb70SBarry Harding 	yge_phy_power(dev, B_TRUE);
7531816cb70SBarry Harding 
7541816cb70SBarry Harding 	/* Reset GPHY/GMAC Control */
7551816cb70SBarry Harding 	for (i = 0; i < dev->d_num_port; i++) {
7561816cb70SBarry Harding 		/* GPHY Control reset. */
7571816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(i, GPHY_CTRL), GPC_RST_SET);
7581816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(i, GPHY_CTRL), GPC_RST_CLR);
7591816cb70SBarry Harding 		/* GMAC Control reset. */
7601816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(i, GMAC_CTRL), GMC_RST_SET);
7611816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(i, GMAC_CTRL), GMC_RST_CLR);
7621816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_EX ||
7631816cb70SBarry Harding 		    dev->d_hw_id == CHIP_ID_YUKON_SUPR) {
7641816cb70SBarry Harding 			CSR_WRITE_2(dev, MR_ADDR(i, GMAC_CTRL),
7651816cb70SBarry Harding 			    (GMC_BYP_RETR_ON | GMC_BYP_MACSECRX_ON |
7661816cb70SBarry Harding 			    GMC_BYP_MACSECTX_ON));
7671816cb70SBarry Harding 		}
7681816cb70SBarry Harding 		CSR_WRITE_2(dev, MR_ADDR(i, GMAC_CTRL), GMC_F_LOOPB_OFF);
7691816cb70SBarry Harding 
7701816cb70SBarry Harding 	}
7711816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
7721816cb70SBarry Harding 
7731816cb70SBarry Harding 	/* LED On. */
7741816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, Y2_LED_STAT_ON);
7751816cb70SBarry Harding 
7761816cb70SBarry Harding 	/* Clear TWSI IRQ. */
7771816cb70SBarry Harding 	CSR_WRITE_4(dev, B2_I2C_IRQ, I2C_CLR_IRQ);
7781816cb70SBarry Harding 
7791816cb70SBarry Harding 	/* Turn off hardware timer. */
7801816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TI_CTRL, TIM_STOP);
7811816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TI_CTRL, TIM_CLR_IRQ);
7821816cb70SBarry Harding 
7831816cb70SBarry Harding 	/* Turn off descriptor polling. */
7841816cb70SBarry Harding 	CSR_WRITE_1(dev, B28_DPT_CTRL, DPT_STOP);
7851816cb70SBarry Harding 
7861816cb70SBarry Harding 	/* Turn off time stamps. */
7871816cb70SBarry Harding 	CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_STOP);
7881816cb70SBarry Harding 	CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
7891816cb70SBarry Harding 
7901816cb70SBarry Harding 	/* Don't permit config space writing */
7911816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
7921816cb70SBarry Harding 
7931816cb70SBarry Harding 	/* enable TX Arbiters */
7941816cb70SBarry Harding 	for (i = 0; i < dev->d_num_port; i++)
7951816cb70SBarry Harding 		CSR_WRITE_1(dev, MR_ADDR(i, TXA_CTRL), TXA_ENA_ARB);
7961816cb70SBarry Harding 
7971816cb70SBarry Harding 	/* Configure timeout values. */
7981816cb70SBarry Harding 	for (i = 0; i < dev->d_num_port; i++) {
7991816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR);
8001816cb70SBarry Harding 
8011816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_R1), RI_TO_53);
8021816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XA1), RI_TO_53);
8031816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XS1), RI_TO_53);
8041816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_R1), RI_TO_53);
8051816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XA1), RI_TO_53);
8061816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XS1), RI_TO_53);
8071816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_R2), RI_TO_53);
8081816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XA2), RI_TO_53);
8091816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_WTO_XS2), RI_TO_53);
8101816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_R2), RI_TO_53);
8111816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XA2), RI_TO_53);
8121816cb70SBarry Harding 		CSR_WRITE_1(dev, SELECT_RAM_BUFFER(i, B3_RI_RTO_XS2), RI_TO_53);
8131816cb70SBarry Harding 	}
8141816cb70SBarry Harding 
8151816cb70SBarry Harding 	/* Disable all interrupts. */
8161816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
8171816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
8181816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, 0);
8191816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
8201816cb70SBarry Harding 
8211816cb70SBarry Harding 	/*
8221816cb70SBarry Harding 	 * On dual port PCI-X card, there is an problem where status
8231816cb70SBarry Harding 	 * can be received out of order due to split transactions.
8241816cb70SBarry Harding 	 */
8251816cb70SBarry Harding 	if (dev->d_bustype == PCIX_BUS && dev->d_num_port > 1) {
8261816cb70SBarry Harding 		int pcix;
8271816cb70SBarry Harding 		uint16_t pcix_cmd;
8281816cb70SBarry Harding 
8291816cb70SBarry Harding 		if ((pcix = yge_find_capability(dev, PCI_CAP_ID_PCIX)) != 0) {
8301816cb70SBarry Harding 			pcix_cmd = pci_config_get16(pcih, pcix + 2);
8311816cb70SBarry Harding 			/* Clear Max Outstanding Split Transactions. */
8321816cb70SBarry Harding 			pcix_cmd &= ~0x70;
8331816cb70SBarry Harding 			CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
8341816cb70SBarry Harding 			pci_config_put16(pcih, pcix + 2, pcix_cmd);
8351816cb70SBarry Harding 			CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
8361816cb70SBarry Harding 		}
8371816cb70SBarry Harding 	}
8381816cb70SBarry Harding 	if (dev->d_bustype == PEX_BUS) {
8391816cb70SBarry Harding 		uint16_t v, width;
8401816cb70SBarry Harding 
8411816cb70SBarry Harding 		v = pci_config_get16(pcih, PEX_DEV_CTRL);
8421816cb70SBarry Harding 		/* Change Max. Read Request Size to 4096 bytes. */
8431816cb70SBarry Harding 		v &= ~PEX_DC_MAX_RRS_MSK;
8441816cb70SBarry Harding 		v |= PEX_DC_MAX_RD_RQ_SIZE(5);
8451816cb70SBarry Harding 		pci_config_put16(pcih, PEX_DEV_CTRL, v);
8461816cb70SBarry Harding 		width = pci_config_get16(pcih, PEX_LNK_STAT);
8471816cb70SBarry Harding 		width = (width & PEX_LS_LINK_WI_MSK) >> 4;
8481816cb70SBarry Harding 		v = pci_config_get16(pcih, PEX_LNK_CAP);
8491816cb70SBarry Harding 		v = (v & PEX_LS_LINK_WI_MSK) >> 4;
8501816cb70SBarry Harding 		if (v != width)
8511816cb70SBarry Harding 			yge_error(dev, NULL,
8521816cb70SBarry Harding 			    "Negotiated width of PCIe link(x%d) != "
8531816cb70SBarry Harding 			    "max. width of link(x%d)\n", width, v);
8541816cb70SBarry Harding 	}
8551816cb70SBarry Harding 
8561816cb70SBarry Harding 	/* Clear status list. */
8571816cb70SBarry Harding 	CLEARRING(&dev->d_status_ring);
8581816cb70SBarry Harding 	SYNCRING(&dev->d_status_ring, DDI_DMA_SYNC_FORDEV);
8591816cb70SBarry Harding 
8601816cb70SBarry Harding 	dev->d_stat_cons = 0;
8611816cb70SBarry Harding 
8621816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_RST_SET);
8631816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_RST_CLR);
8641816cb70SBarry Harding 
8651816cb70SBarry Harding 	/* Set the status list base address. */
8661816cb70SBarry Harding 	addr = dev->d_status_ring.r_paddr;
8671816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_LIST_ADDR_LO, YGE_ADDR_LO(addr));
8681816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_LIST_ADDR_HI, YGE_ADDR_HI(addr));
8691816cb70SBarry Harding 
8701816cb70SBarry Harding 	/* Set the status list last index. */
8711816cb70SBarry Harding 	CSR_WRITE_2(dev, STAT_LAST_IDX, YGE_STAT_RING_CNT - 1);
8721816cb70SBarry Harding 	CSR_WRITE_2(dev, STAT_PUT_IDX, 0);
8731816cb70SBarry Harding 
8741816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_EC &&
8751816cb70SBarry Harding 	    dev->d_hw_rev == CHIP_REV_YU_EC_A1) {
8761816cb70SBarry Harding 		/* WA for dev. #4.3 */
8771816cb70SBarry Harding 		CSR_WRITE_2(dev, STAT_TX_IDX_TH, ST_TXTH_IDX_MASK);
8781816cb70SBarry Harding 		/* WA for dev #4.18 */
8791816cb70SBarry Harding 		CSR_WRITE_1(dev, STAT_FIFO_WM, 0x21);
8801816cb70SBarry Harding 		CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 7);
8811816cb70SBarry Harding 	} else {
8821816cb70SBarry Harding 		CSR_WRITE_2(dev, STAT_TX_IDX_TH, 10);
8831816cb70SBarry Harding 		CSR_WRITE_1(dev, STAT_FIFO_WM, 16);
8841816cb70SBarry Harding 
8851816cb70SBarry Harding 		/* ISR status FIFO watermark */
8861816cb70SBarry Harding 		if (dev->d_hw_id == CHIP_ID_YUKON_XL &&
8871816cb70SBarry Harding 		    dev->d_hw_rev == CHIP_REV_YU_XL_A0)
8881816cb70SBarry Harding 			CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 4);
8891816cb70SBarry Harding 		else
8901816cb70SBarry Harding 			CSR_WRITE_1(dev, STAT_FIFO_ISR_WM, 16);
8911816cb70SBarry Harding 
8921816cb70SBarry Harding 		CSR_WRITE_4(dev, STAT_ISR_TIMER_INI, 0x0190);
8931816cb70SBarry Harding 	}
8941816cb70SBarry Harding 
8951816cb70SBarry Harding 	/*
8961816cb70SBarry Harding 	 * Use default value for STAT_ISR_TIMER_INI, STAT_LEV_TIMER_INI.
8971816cb70SBarry Harding 	 */
8981816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_TX_TIMER_INI, YGE_USECS(dev, 1000));
8991816cb70SBarry Harding 
9001816cb70SBarry Harding 	/* Enable status unit. */
9011816cb70SBarry Harding 	CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_OP_ON);
9021816cb70SBarry Harding 
9031816cb70SBarry Harding 	CSR_WRITE_1(dev, STAT_TX_TIMER_CTRL, TIM_START);
9041816cb70SBarry Harding 	CSR_WRITE_1(dev, STAT_LEV_TIMER_CTRL, TIM_START);
9051816cb70SBarry Harding 	CSR_WRITE_1(dev, STAT_ISR_TIMER_CTRL, TIM_START);
9061816cb70SBarry Harding }
9071816cb70SBarry Harding 
9081816cb70SBarry Harding static int
9091816cb70SBarry Harding yge_init_port(yge_port_t *port)
9101816cb70SBarry Harding {
9111816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
9121816cb70SBarry Harding 	int i;
9131816cb70SBarry Harding 	mac_register_t *macp;
9141816cb70SBarry Harding 
9151816cb70SBarry Harding 	port->p_flags = dev->d_pflags;
9161816cb70SBarry Harding 	port->p_ppa = ddi_get_instance(dev->d_dip) + (port->p_port * 100);
9171816cb70SBarry Harding 
9181816cb70SBarry Harding 	port->p_tx_buf = kmem_zalloc(sizeof (yge_buf_t) * YGE_TX_RING_CNT,
9191816cb70SBarry Harding 	    KM_SLEEP);
9201816cb70SBarry Harding 	port->p_rx_buf = kmem_zalloc(sizeof (yge_buf_t) * YGE_RX_RING_CNT,
9211816cb70SBarry Harding 	    KM_SLEEP);
9221816cb70SBarry Harding 
9231816cb70SBarry Harding 	/* Setup Tx/Rx queue register offsets. */
9241816cb70SBarry Harding 	if (port->p_port == YGE_PORT_A) {
9251816cb70SBarry Harding 		port->p_txq = Q_XA1;
9261816cb70SBarry Harding 		port->p_txsq = Q_XS1;
9271816cb70SBarry Harding 		port->p_rxq = Q_R1;
9281816cb70SBarry Harding 	} else {
9291816cb70SBarry Harding 		port->p_txq = Q_XA2;
9301816cb70SBarry Harding 		port->p_txsq = Q_XS2;
9311816cb70SBarry Harding 		port->p_rxq = Q_R2;
9321816cb70SBarry Harding 	}
9331816cb70SBarry Harding 
9341816cb70SBarry Harding 	/* Disable jumbo frame for Yukon FE. */
9351816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_FE)
9361816cb70SBarry Harding 		port->p_flags |= PORT_FLAG_NOJUMBO;
9371816cb70SBarry Harding 
9381816cb70SBarry Harding 	/*
9391816cb70SBarry Harding 	 * Start out assuming a regular MTU.  Users can change this
9401816cb70SBarry Harding 	 * with dladm.  The dladm daemon is supposed to issue commands
9411816cb70SBarry Harding 	 * to change the default MTU using m_setprop during early boot
9421816cb70SBarry Harding 	 * (before the interface is plumbed) if the user has so
9431816cb70SBarry Harding 	 * requested.
9441816cb70SBarry Harding 	 */
9451816cb70SBarry Harding 	port->p_mtu = ETHERMTU;
9461816cb70SBarry Harding 
9471816cb70SBarry Harding 	port->p_mii = mii_alloc(port, dev->d_dip, &yge_mii_ops);
9481816cb70SBarry Harding 	if (port->p_mii == NULL) {
9491816cb70SBarry Harding 		yge_error(NULL, port, "MII handle allocation failed");
9501816cb70SBarry Harding 		return (DDI_FAILURE);
9511816cb70SBarry Harding 	}
9521816cb70SBarry Harding 	/* We assume all parts support asymmetric pause */
9531816cb70SBarry Harding 	mii_set_pauseable(port->p_mii, B_TRUE, B_TRUE);
9541816cb70SBarry Harding 
9551816cb70SBarry Harding 	/*
9561816cb70SBarry Harding 	 * Get station address for this interface. Note that
9571816cb70SBarry Harding 	 * dual port cards actually come with three station
9581816cb70SBarry Harding 	 * addresses: one for each port, plus an extra. The
9591816cb70SBarry Harding 	 * extra one is used by the SysKonnect driver software
9601816cb70SBarry Harding 	 * as a 'virtual' station address for when both ports
9611816cb70SBarry Harding 	 * are operating in failover mode. Currently we don't
9621816cb70SBarry Harding 	 * use this extra address.
9631816cb70SBarry Harding 	 */
9641816cb70SBarry Harding 	for (i = 0; i < ETHERADDRL; i++) {
9651816cb70SBarry Harding 		port->p_curraddr[i] =
9661816cb70SBarry Harding 		    CSR_READ_1(dev, B2_MAC_1 + (port->p_port * 8) + i);
9671816cb70SBarry Harding 	}
9681816cb70SBarry Harding 
9691816cb70SBarry Harding 	/* Register with Nemo. */
9701816cb70SBarry Harding 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
9711816cb70SBarry Harding 		yge_error(NULL, port, "MAC handle allocation failed");
9721816cb70SBarry Harding 		return (DDI_FAILURE);
9731816cb70SBarry Harding 	}
9741816cb70SBarry Harding 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
9751816cb70SBarry Harding 	macp->m_driver = port;
9761816cb70SBarry Harding 	macp->m_dip = dev->d_dip;
9771816cb70SBarry Harding 	macp->m_src_addr = port->p_curraddr;
9781816cb70SBarry Harding 	macp->m_callbacks = &yge_m_callbacks;
9791816cb70SBarry Harding 	macp->m_min_sdu = 0;
9801816cb70SBarry Harding 	macp->m_max_sdu = port->p_mtu;
9811816cb70SBarry Harding 	macp->m_instance = port->p_ppa;
9821816cb70SBarry Harding 	macp->m_margin = VLAN_TAGSZ;
9831816cb70SBarry Harding 
9841816cb70SBarry Harding 	port->p_mreg = macp;
9851816cb70SBarry Harding 
9861816cb70SBarry Harding 	return (DDI_SUCCESS);
9871816cb70SBarry Harding }
9881816cb70SBarry Harding 
9891816cb70SBarry Harding static int
9901816cb70SBarry Harding yge_add_intr(yge_dev_t *dev, int intr_type)
9911816cb70SBarry Harding {
9921816cb70SBarry Harding 	dev_info_t		*dip;
9931816cb70SBarry Harding 	int			count;
9941816cb70SBarry Harding 	int			actual;
9951816cb70SBarry Harding 	int			rv;
9961816cb70SBarry Harding 	int 			i, j;
9971816cb70SBarry Harding 
9981816cb70SBarry Harding 	dip = dev->d_dip;
9991816cb70SBarry Harding 
10001816cb70SBarry Harding 	rv = ddi_intr_get_nintrs(dip, intr_type, &count);
10011816cb70SBarry Harding 	if ((rv != DDI_SUCCESS) || (count == 0)) {
10021816cb70SBarry Harding 		yge_error(dev, NULL,
10031816cb70SBarry Harding 		    "ddi_intr_get_nintrs failed, rv %d, count %d", rv, count);
10041816cb70SBarry Harding 		return (DDI_FAILURE);
10051816cb70SBarry Harding 	}
10061816cb70SBarry Harding 
10071816cb70SBarry Harding 	/*
10081816cb70SBarry Harding 	 * Allocate the interrupt.  Note that we only bother with a single
10091816cb70SBarry Harding 	 * interrupt.  One could argue that for MSI devices with dual ports,
10101816cb70SBarry Harding 	 * it would be nice to have a separate interrupt per port.  But right
10111816cb70SBarry Harding 	 * now I don't know how to configure that, so we'll just settle for
10121816cb70SBarry Harding 	 * a single interrupt.
10131816cb70SBarry Harding 	 */
10141816cb70SBarry Harding 	dev->d_intrcnt = 1;
10151816cb70SBarry Harding 
10161816cb70SBarry Harding 	dev->d_intrsize = count * sizeof (ddi_intr_handle_t);
10171816cb70SBarry Harding 	dev->d_intrh = kmem_zalloc(dev->d_intrsize, KM_SLEEP);
10181816cb70SBarry Harding 	if (dev->d_intrh == NULL) {
10191816cb70SBarry Harding 		yge_error(dev, NULL, "Unable to allocate interrupt handle");
10201816cb70SBarry Harding 		return (DDI_FAILURE);
10211816cb70SBarry Harding 	}
10221816cb70SBarry Harding 
10231816cb70SBarry Harding 	rv = ddi_intr_alloc(dip, dev->d_intrh, intr_type, 0, dev->d_intrcnt,
10241816cb70SBarry Harding 	    &actual, DDI_INTR_ALLOC_STRICT);
10251816cb70SBarry Harding 	if ((rv != DDI_SUCCESS) || (actual == 0)) {
10261816cb70SBarry Harding 		yge_error(dev, NULL,
10271816cb70SBarry Harding 		    "Unable to allocate interrupt, %d, count %d",
10281816cb70SBarry Harding 		    rv, actual);
10291816cb70SBarry Harding 		kmem_free(dev->d_intrh, dev->d_intrsize);
10301816cb70SBarry Harding 		return (DDI_FAILURE);
10311816cb70SBarry Harding 	}
10321816cb70SBarry Harding 
10331816cb70SBarry Harding 	if ((rv = ddi_intr_get_pri(dev->d_intrh[0], &dev->d_intrpri)) !=
10341816cb70SBarry Harding 	    DDI_SUCCESS) {
10351816cb70SBarry Harding 		for (i = 0; i < dev->d_intrcnt; i++)
10361816cb70SBarry Harding 			(void) ddi_intr_free(dev->d_intrh[i]);
10371816cb70SBarry Harding 		yge_error(dev, NULL,
10381816cb70SBarry Harding 		    "Unable to get interrupt priority, %d", rv);
10391816cb70SBarry Harding 		kmem_free(dev->d_intrh, dev->d_intrsize);
10401816cb70SBarry Harding 		return (DDI_FAILURE);
10411816cb70SBarry Harding 	}
10421816cb70SBarry Harding 
10431816cb70SBarry Harding 	if ((rv = ddi_intr_get_cap(dev->d_intrh[0], &dev->d_intrcap)) !=
10441816cb70SBarry Harding 	    DDI_SUCCESS) {
10451816cb70SBarry Harding 		yge_error(dev, NULL,
10461816cb70SBarry Harding 		    "Unable to get interrupt capabilities, %d", rv);
10471816cb70SBarry Harding 		for (i = 0; i < dev->d_intrcnt; i++)
10481816cb70SBarry Harding 			(void) ddi_intr_free(dev->d_intrh[i]);
10491816cb70SBarry Harding 		kmem_free(dev->d_intrh, dev->d_intrsize);
10501816cb70SBarry Harding 		return (DDI_FAILURE);
10511816cb70SBarry Harding 	}
10521816cb70SBarry Harding 
10531816cb70SBarry Harding 	/* register interrupt handler to kernel */
10541816cb70SBarry Harding 	for (i = 0; i < dev->d_intrcnt; i++) {
10551816cb70SBarry Harding 		if ((rv = ddi_intr_add_handler(dev->d_intrh[i], yge_intr,
10561816cb70SBarry Harding 		    dev, NULL)) != DDI_SUCCESS) {
10571816cb70SBarry Harding 			yge_error(dev, NULL,
10581816cb70SBarry Harding 			    "Unable to add interrupt handler, %d", rv);
10591816cb70SBarry Harding 			for (j = 0; j < i; j++)
10601816cb70SBarry Harding 				(void) ddi_intr_remove_handler(dev->d_intrh[j]);
10611816cb70SBarry Harding 			for (i = 0; i < dev->d_intrcnt; i++)
10621816cb70SBarry Harding 				(void) ddi_intr_free(dev->d_intrh[i]);
10631816cb70SBarry Harding 			kmem_free(dev->d_intrh, dev->d_intrsize);
10641816cb70SBarry Harding 			return (DDI_FAILURE);
10651816cb70SBarry Harding 		}
10661816cb70SBarry Harding 	}
10671816cb70SBarry Harding 
10681816cb70SBarry Harding 	mutex_init(&dev->d_rxlock, NULL, MUTEX_DRIVER,
10691816cb70SBarry Harding 	    DDI_INTR_PRI(dev->d_intrpri));
10701816cb70SBarry Harding 	mutex_init(&dev->d_txlock, NULL, MUTEX_DRIVER,
10711816cb70SBarry Harding 	    DDI_INTR_PRI(dev->d_intrpri));
10721816cb70SBarry Harding 	mutex_init(&dev->d_phylock, NULL, MUTEX_DRIVER,
10731816cb70SBarry Harding 	    DDI_INTR_PRI(dev->d_intrpri));
10741816cb70SBarry Harding 	mutex_init(&dev->d_task_mtx, NULL, MUTEX_DRIVER,
10751816cb70SBarry Harding 	    DDI_INTR_PRI(dev->d_intrpri));
10761816cb70SBarry Harding 
10771816cb70SBarry Harding 	return (DDI_SUCCESS);
10781816cb70SBarry Harding }
10791816cb70SBarry Harding 
10801816cb70SBarry Harding static int
10811816cb70SBarry Harding yge_attach_intr(yge_dev_t *dev)
10821816cb70SBarry Harding {
10831816cb70SBarry Harding 	dev_info_t *dip = dev->d_dip;
10841816cb70SBarry Harding 	int intr_types;
10851816cb70SBarry Harding 	int rv;
10861816cb70SBarry Harding 
10871816cb70SBarry Harding 	/* Allocate IRQ resources. */
10881816cb70SBarry Harding 	rv = ddi_intr_get_supported_types(dip, &intr_types);
10891816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
10901816cb70SBarry Harding 		yge_error(dev, NULL,
10911816cb70SBarry Harding 		    "Unable to determine supported interrupt types, %d", rv);
10921816cb70SBarry Harding 		return (DDI_FAILURE);
10931816cb70SBarry Harding 	}
10941816cb70SBarry Harding 
10951816cb70SBarry Harding 	/*
10961816cb70SBarry Harding 	 * We default to not supporting MSI.  We've found some device
10971816cb70SBarry Harding 	 * and motherboard combinations don't always work well with
10981816cb70SBarry Harding 	 * MSI interrupts.  Users may override this if they choose.
10991816cb70SBarry Harding 	 */
11001816cb70SBarry Harding 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "msi_enable", 0) == 0) {
11011816cb70SBarry Harding 		/* If msi disable property present, disable both msix/msi. */
11021816cb70SBarry Harding 		if (intr_types & DDI_INTR_TYPE_FIXED) {
11031816cb70SBarry Harding 			intr_types &= ~(DDI_INTR_TYPE_MSI | DDI_INTR_TYPE_MSIX);
11041816cb70SBarry Harding 		}
11051816cb70SBarry Harding 	}
11061816cb70SBarry Harding 
11071816cb70SBarry Harding 	if (intr_types & DDI_INTR_TYPE_MSIX) {
11081816cb70SBarry Harding 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_MSIX)) ==
11091816cb70SBarry Harding 		    DDI_SUCCESS)
11101816cb70SBarry Harding 			return (DDI_SUCCESS);
11111816cb70SBarry Harding 	}
11121816cb70SBarry Harding 
11131816cb70SBarry Harding 	if (intr_types & DDI_INTR_TYPE_MSI) {
11141816cb70SBarry Harding 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_MSI)) ==
11151816cb70SBarry Harding 		    DDI_SUCCESS)
11161816cb70SBarry Harding 			return (DDI_SUCCESS);
11171816cb70SBarry Harding 	}
11181816cb70SBarry Harding 
11191816cb70SBarry Harding 	if (intr_types & DDI_INTR_TYPE_FIXED) {
11201816cb70SBarry Harding 		if ((rv = yge_add_intr(dev, DDI_INTR_TYPE_FIXED)) ==
11211816cb70SBarry Harding 		    DDI_SUCCESS)
11221816cb70SBarry Harding 			return (DDI_SUCCESS);
11231816cb70SBarry Harding 	}
11241816cb70SBarry Harding 
11251816cb70SBarry Harding 	yge_error(dev, NULL, "Unable to configure any interrupts");
11261816cb70SBarry Harding 	return (DDI_FAILURE);
11271816cb70SBarry Harding }
11281816cb70SBarry Harding 
11291816cb70SBarry Harding static void
11301816cb70SBarry Harding yge_intr_enable(yge_dev_t *dev)
11311816cb70SBarry Harding {
11321816cb70SBarry Harding 	int i;
11331816cb70SBarry Harding 	if (dev->d_intrcap & DDI_INTR_FLAG_BLOCK) {
11341816cb70SBarry Harding 		/* Call ddi_intr_block_enable() for MSI interrupts */
11351816cb70SBarry Harding 		(void) ddi_intr_block_enable(dev->d_intrh, dev->d_intrcnt);
11361816cb70SBarry Harding 	} else {
11371816cb70SBarry Harding 		/* Call ddi_intr_enable for FIXED interrupts */
11381816cb70SBarry Harding 		for (i = 0; i < dev->d_intrcnt; i++)
11391816cb70SBarry Harding 			(void) ddi_intr_enable(dev->d_intrh[i]);
11401816cb70SBarry Harding 	}
11411816cb70SBarry Harding }
11421816cb70SBarry Harding 
11431816cb70SBarry Harding void
11441816cb70SBarry Harding yge_intr_disable(yge_dev_t *dev)
11451816cb70SBarry Harding {
11461816cb70SBarry Harding 	int i;
11471816cb70SBarry Harding 
11481816cb70SBarry Harding 	if (dev->d_intrcap & DDI_INTR_FLAG_BLOCK) {
11491816cb70SBarry Harding 		(void) ddi_intr_block_disable(dev->d_intrh, dev->d_intrcnt);
11501816cb70SBarry Harding 	} else {
11511816cb70SBarry Harding 		for (i = 0; i < dev->d_intrcnt; i++)
11521816cb70SBarry Harding 			(void) ddi_intr_disable(dev->d_intrh[i]);
11531816cb70SBarry Harding 	}
11541816cb70SBarry Harding }
11551816cb70SBarry Harding 
11561816cb70SBarry Harding static uint8_t
11571816cb70SBarry Harding yge_find_capability(yge_dev_t *dev, uint8_t cap)
11581816cb70SBarry Harding {
11591816cb70SBarry Harding 	uint8_t ptr;
11601816cb70SBarry Harding 	uint16_t capit;
11611816cb70SBarry Harding 	ddi_acc_handle_t pcih = dev->d_pcih;
11621816cb70SBarry Harding 
11631816cb70SBarry Harding 	if ((pci_config_get16(pcih, PCI_CONF_STAT) & PCI_STAT_CAP) == 0) {
11641816cb70SBarry Harding 		return (0);
11651816cb70SBarry Harding 	}
11661816cb70SBarry Harding 	/* This assumes PCI, and not CardBus. */
11671816cb70SBarry Harding 	ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
11681816cb70SBarry Harding 	while (ptr != 0) {
11691816cb70SBarry Harding 		capit = pci_config_get8(pcih, ptr + PCI_CAP_ID);
11701816cb70SBarry Harding 		if (capit == cap) {
11711816cb70SBarry Harding 			return (ptr);
11721816cb70SBarry Harding 		}
11731816cb70SBarry Harding 		ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR);
11741816cb70SBarry Harding 	}
11751816cb70SBarry Harding 	return (0);
11761816cb70SBarry Harding }
11771816cb70SBarry Harding 
11781816cb70SBarry Harding static int
11791816cb70SBarry Harding yge_attach(yge_dev_t *dev)
11801816cb70SBarry Harding {
11811816cb70SBarry Harding 	dev_info_t	*dip = dev->d_dip;
11821816cb70SBarry Harding 	int		rv;
11831816cb70SBarry Harding 	int		nattached;
11841816cb70SBarry Harding 	uint8_t		pm_cap;
11851816cb70SBarry Harding 
11861816cb70SBarry Harding 	if (pci_config_setup(dip, &dev->d_pcih) != DDI_SUCCESS) {
11871816cb70SBarry Harding 		yge_error(dev, NULL, "Unable to map PCI configuration space");
11881816cb70SBarry Harding 		goto fail;
11891816cb70SBarry Harding 	}
11901816cb70SBarry Harding 
11911816cb70SBarry Harding 	/*
11921816cb70SBarry Harding 	 * Map control/status registers.
11931816cb70SBarry Harding 	 */
11941816cb70SBarry Harding 
11951816cb70SBarry Harding 	/* ensure the pmcsr status is D0 state */
11961816cb70SBarry Harding 	pm_cap = yge_find_capability(dev, PCI_CAP_ID_PM);
11971816cb70SBarry Harding 	if (pm_cap != 0) {
11981816cb70SBarry Harding 		uint16_t pmcsr;
11991816cb70SBarry Harding 		pmcsr = pci_config_get16(dev->d_pcih, pm_cap + PCI_PMCSR);
12001816cb70SBarry Harding 		pmcsr &= ~PCI_PMCSR_STATE_MASK;
12011816cb70SBarry Harding 		pci_config_put16(dev->d_pcih, pm_cap + PCI_PMCSR,
12021816cb70SBarry Harding 		    pmcsr | PCI_PMCSR_D0);
12031816cb70SBarry Harding 	}
12041816cb70SBarry Harding 
12051816cb70SBarry Harding 	/* Enable PCI access and bus master. */
12061816cb70SBarry Harding 	pci_config_put16(dev->d_pcih, PCI_CONF_COMM,
12071816cb70SBarry Harding 	    pci_config_get16(dev->d_pcih, PCI_CONF_COMM) |
12081816cb70SBarry Harding 	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
12091816cb70SBarry Harding 
12101816cb70SBarry Harding 
12111816cb70SBarry Harding 	/* Allocate I/O resource */
12121816cb70SBarry Harding 	rv = ddi_regs_map_setup(dip, 1, &dev->d_regs, 0, 0, &yge_regs_attr,
12131816cb70SBarry Harding 	    &dev->d_regsh);
12141816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
12151816cb70SBarry Harding 		yge_error(dev, NULL, "Unable to map device registers");
12161816cb70SBarry Harding 		goto fail;
12171816cb70SBarry Harding 	}
12181816cb70SBarry Harding 
12191816cb70SBarry Harding 
12201816cb70SBarry Harding 	/* Enable all clocks. */
12211816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
12221816cb70SBarry Harding 	pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
12231816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
12241816cb70SBarry Harding 
12251816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_CLR);
12261816cb70SBarry Harding 	dev->d_hw_id = CSR_READ_1(dev, B2_CHIP_ID);
12271816cb70SBarry Harding 	dev->d_hw_rev = (CSR_READ_1(dev, B2_MAC_CFG) >> 4) & 0x0f;
12281816cb70SBarry Harding 
12291816cb70SBarry Harding 
12301816cb70SBarry Harding 	/*
12311816cb70SBarry Harding 	 * Bail out if chip is not recognized.  Note that we only enforce
12321816cb70SBarry Harding 	 * this in production builds.  The Ultra-2 (88e8057) has a problem
12331816cb70SBarry Harding 	 * right now where TX works fine, but RX seems not to.  So we've
12341816cb70SBarry Harding 	 * disabled that for now.
12351816cb70SBarry Harding 	 */
12361816cb70SBarry Harding 	if (dev->d_hw_id < CHIP_ID_YUKON_XL ||
12371816cb70SBarry Harding 	    dev->d_hw_id >= CHIP_ID_YUKON_UL_2) {
12381816cb70SBarry Harding 		yge_error(dev, NULL, "Unknown device: id=0x%02x, rev=0x%02x",
12391816cb70SBarry Harding 		    dev->d_hw_id, dev->d_hw_rev);
12401816cb70SBarry Harding #ifndef	DEBUG
12411816cb70SBarry Harding 		goto fail;
12421816cb70SBarry Harding #endif
12431816cb70SBarry Harding 	}
12441816cb70SBarry Harding 
12451816cb70SBarry Harding 	/* Soft reset. */
12461816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
12471816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_CLR);
12481816cb70SBarry Harding 	dev->d_pmd = CSR_READ_1(dev, B2_PMD_TYP);
12491816cb70SBarry Harding 	if (dev->d_pmd == 'L' || dev->d_pmd == 'S' || dev->d_pmd == 'P')
12501816cb70SBarry Harding 		dev->d_coppertype = 0;
12511816cb70SBarry Harding 	else
12521816cb70SBarry Harding 		dev->d_coppertype = 1;
12531816cb70SBarry Harding 	/* Check number of MACs. */
12541816cb70SBarry Harding 	dev->d_num_port = 1;
12551816cb70SBarry Harding 	if ((CSR_READ_1(dev, B2_Y2_HW_RES) & CFG_DUAL_MAC_MSK) ==
12561816cb70SBarry Harding 	    CFG_DUAL_MAC_MSK) {
12571816cb70SBarry Harding 		if (!(CSR_READ_1(dev, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC))
12581816cb70SBarry Harding 			dev->d_num_port++;
12591816cb70SBarry Harding 	}
12601816cb70SBarry Harding 
12611816cb70SBarry Harding 	/* Check bus type. */
12621816cb70SBarry Harding 	if (yge_find_capability(dev, PCI_CAP_ID_PCI_E) != 0) {
12631816cb70SBarry Harding 		dev->d_bustype = PEX_BUS;
12641816cb70SBarry Harding 	} else if (yge_find_capability(dev, PCI_CAP_ID_PCIX) != 0) {
12651816cb70SBarry Harding 		dev->d_bustype = PCIX_BUS;
12661816cb70SBarry Harding 	} else {
12671816cb70SBarry Harding 		dev->d_bustype = PCI_BUS;
12681816cb70SBarry Harding 	}
12691816cb70SBarry Harding 
12701816cb70SBarry Harding 	switch (dev->d_hw_id) {
12711816cb70SBarry Harding 	case CHIP_ID_YUKON_EC:
12721816cb70SBarry Harding 		dev->d_clock = 125;	/* 125 Mhz */
12731816cb70SBarry Harding 		break;
12741816cb70SBarry Harding 	case CHIP_ID_YUKON_UL_2:
12751816cb70SBarry Harding 		dev->d_clock = 125;	/* 125 Mhz */
12761816cb70SBarry Harding 		break;
12771816cb70SBarry Harding 	case CHIP_ID_YUKON_SUPR:
12781816cb70SBarry Harding 		dev->d_clock = 125;	/* 125 Mhz */
12791816cb70SBarry Harding 		break;
12801816cb70SBarry Harding 	case CHIP_ID_YUKON_EC_U:
12811816cb70SBarry Harding 		dev->d_clock = 125;	/* 125 Mhz */
12821816cb70SBarry Harding 		break;
12831816cb70SBarry Harding 	case CHIP_ID_YUKON_EX:
12841816cb70SBarry Harding 		dev->d_clock = 125;	/* 125 Mhz */
12851816cb70SBarry Harding 		break;
12861816cb70SBarry Harding 	case CHIP_ID_YUKON_FE:
12871816cb70SBarry Harding 		dev->d_clock = 100;	/* 100 Mhz */
12881816cb70SBarry Harding 		break;
12891816cb70SBarry Harding 	case CHIP_ID_YUKON_FE_P:
12901816cb70SBarry Harding 		dev->d_clock = 50;	/* 50 Mhz */
12911816cb70SBarry Harding 		break;
12921816cb70SBarry Harding 	case CHIP_ID_YUKON_XL:
12931816cb70SBarry Harding 		dev->d_clock = 156;	/* 156 Mhz */
12941816cb70SBarry Harding 		break;
12951816cb70SBarry Harding 	default:
12961816cb70SBarry Harding 		dev->d_clock = 156;	/* 156 Mhz */
12971816cb70SBarry Harding 		break;
12981816cb70SBarry Harding 	}
12991816cb70SBarry Harding 
13001816cb70SBarry Harding 	dev->d_process_limit = YGE_RX_RING_CNT/2;
13011816cb70SBarry Harding 
13021816cb70SBarry Harding 	rv = yge_alloc_ring(NULL, dev, &dev->d_status_ring, YGE_STAT_RING_CNT);
13031816cb70SBarry Harding 	if (rv != DDI_SUCCESS)
13041816cb70SBarry Harding 		goto fail;
13051816cb70SBarry Harding 
13061816cb70SBarry Harding 	/* Setup event taskq. */
13071816cb70SBarry Harding 	dev->d_task_q = ddi_taskq_create(dip, "tq", 1, TASKQ_DEFAULTPRI, 0);
13081816cb70SBarry Harding 	if (dev->d_task_q == NULL) {
13091816cb70SBarry Harding 		yge_error(dev, NULL, "failed to create taskq");
13101816cb70SBarry Harding 		goto fail;
13111816cb70SBarry Harding 	}
13121816cb70SBarry Harding 
13131816cb70SBarry Harding 	/* Init the condition variable */
13141816cb70SBarry Harding 	cv_init(&dev->d_task_cv, NULL, CV_DRIVER, NULL);
13151816cb70SBarry Harding 
13161816cb70SBarry Harding 	/* Allocate IRQ resources. */
13171816cb70SBarry Harding 	if ((rv = yge_attach_intr(dev)) != DDI_SUCCESS) {
13181816cb70SBarry Harding 		goto fail;
13191816cb70SBarry Harding 	}
13201816cb70SBarry Harding 
13211816cb70SBarry Harding 	/* Set base interrupt mask. */
13221816cb70SBarry Harding 	dev->d_intrmask = Y2_IS_HW_ERR | Y2_IS_STAT_BMU;
13231816cb70SBarry Harding 	dev->d_intrhwemask = Y2_IS_TIST_OV | Y2_IS_MST_ERR |
13241816cb70SBarry Harding 	    Y2_IS_IRQ_STAT | Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP;
13251816cb70SBarry Harding 
13261816cb70SBarry Harding 	/* Reset the adapter. */
13271816cb70SBarry Harding 	yge_reset(dev);
13281816cb70SBarry Harding 
13291816cb70SBarry Harding 	yge_setup_rambuffer(dev);
13301816cb70SBarry Harding 
13311816cb70SBarry Harding 	nattached = 0;
13321816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
13331816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
13341816cb70SBarry Harding 		if (yge_init_port(port) != DDI_SUCCESS) {
13351816cb70SBarry Harding 			goto fail;
13361816cb70SBarry Harding 		}
13371816cb70SBarry Harding 	}
13381816cb70SBarry Harding 
13391816cb70SBarry Harding 	yge_intr_enable(dev);
13401816cb70SBarry Harding 
13411816cb70SBarry Harding 	/* set up the periodic to run once per second */
13421816cb70SBarry Harding 	dev->d_periodic = ddi_periodic_add(yge_tick, dev, 1000000000, 0);
13431816cb70SBarry Harding 
13441816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
13451816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
13461816cb70SBarry Harding 		if (yge_register_port(port) == DDI_SUCCESS) {
13471816cb70SBarry Harding 			nattached++;
13481816cb70SBarry Harding 		}
13491816cb70SBarry Harding 	}
13501816cb70SBarry Harding 
13511816cb70SBarry Harding 	if (nattached == 0) {
13521816cb70SBarry Harding 		goto fail;
13531816cb70SBarry Harding 	}
13541816cb70SBarry Harding 
13551816cb70SBarry Harding 	/* Dispatch the taskq */
13561816cb70SBarry Harding 	if (ddi_taskq_dispatch(dev->d_task_q, yge_task, dev, DDI_SLEEP) !=
13571816cb70SBarry Harding 	    DDI_SUCCESS) {
13581816cb70SBarry Harding 		yge_error(dev, NULL, "failed to start taskq");
13591816cb70SBarry Harding 		goto fail;
13601816cb70SBarry Harding 	}
13611816cb70SBarry Harding 
13621816cb70SBarry Harding 	ddi_report_dev(dip);
13631816cb70SBarry Harding 
13641816cb70SBarry Harding 	return (DDI_SUCCESS);
13651816cb70SBarry Harding 
13661816cb70SBarry Harding fail:
13671816cb70SBarry Harding 	yge_detach(dev);
13681816cb70SBarry Harding 	return (DDI_FAILURE);
13691816cb70SBarry Harding }
13701816cb70SBarry Harding 
13711816cb70SBarry Harding static int
13721816cb70SBarry Harding yge_register_port(yge_port_t *port)
13731816cb70SBarry Harding {
13741816cb70SBarry Harding 	if (mac_register(port->p_mreg, &port->p_mh) != DDI_SUCCESS) {
13751816cb70SBarry Harding 		yge_error(NULL, port, "MAC registration failed");
13761816cb70SBarry Harding 		return (DDI_FAILURE);
13771816cb70SBarry Harding 	}
13781816cb70SBarry Harding 
13791816cb70SBarry Harding 	return (DDI_SUCCESS);
13801816cb70SBarry Harding }
13811816cb70SBarry Harding 
13821816cb70SBarry Harding /*
13831816cb70SBarry Harding  * Free up port specific resources. This is called only when the
13841816cb70SBarry Harding  * port is not registered (and hence not running).
13851816cb70SBarry Harding  */
13861816cb70SBarry Harding static void
13871816cb70SBarry Harding yge_uninit_port(yge_port_t *port)
13881816cb70SBarry Harding {
13891816cb70SBarry Harding 	ASSERT(!port->p_running);
13901816cb70SBarry Harding 
13911816cb70SBarry Harding 	if (port->p_mreg)
13921816cb70SBarry Harding 		mac_free(port->p_mreg);
13931816cb70SBarry Harding 
13941816cb70SBarry Harding 	if (port->p_mii)
13951816cb70SBarry Harding 		mii_free(port->p_mii);
13961816cb70SBarry Harding 
13971816cb70SBarry Harding 	yge_txrx_dma_free(port);
13981816cb70SBarry Harding 
13991816cb70SBarry Harding 	if (port->p_tx_buf)
14001816cb70SBarry Harding 		kmem_free(port->p_tx_buf,
14011816cb70SBarry Harding 		    sizeof (yge_buf_t) * YGE_TX_RING_CNT);
14021816cb70SBarry Harding 	if (port->p_rx_buf)
14031816cb70SBarry Harding 		kmem_free(port->p_rx_buf,
14041816cb70SBarry Harding 		    sizeof (yge_buf_t) * YGE_RX_RING_CNT);
14051816cb70SBarry Harding }
14061816cb70SBarry Harding 
14071816cb70SBarry Harding static void
14081816cb70SBarry Harding yge_detach(yge_dev_t *dev)
14091816cb70SBarry Harding {
14101816cb70SBarry Harding 	/*
14111816cb70SBarry Harding 	 * Turn off the periodic.
14121816cb70SBarry Harding 	 */
14131816cb70SBarry Harding 	if (dev->d_periodic)
14141816cb70SBarry Harding 		ddi_periodic_delete(dev->d_periodic);
14151816cb70SBarry Harding 
14161816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
14171816cb70SBarry Harding 		yge_uninit_port(dev->d_port[i]);
14181816cb70SBarry Harding 	}
14191816cb70SBarry Harding 
14201816cb70SBarry Harding 	/*
14211816cb70SBarry Harding 	 * Make sure all interrupts are disabled.
14221816cb70SBarry Harding 	 */
14231816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, 0);
14241816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
14251816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
14261816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
14271816cb70SBarry Harding 
14281816cb70SBarry Harding 	/* LED Off. */
14291816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, Y2_LED_STAT_OFF);
14301816cb70SBarry Harding 
14311816cb70SBarry Harding 	/* Put hardware reset. */
14321816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
14331816cb70SBarry Harding 
14341816cb70SBarry Harding 	yge_free_ring(&dev->d_status_ring);
14351816cb70SBarry Harding 
14361816cb70SBarry Harding 	if (dev->d_task_q != NULL) {
14371816cb70SBarry Harding 		yge_dispatch(dev, YGE_TASK_EXIT);
14381816cb70SBarry Harding 		ddi_taskq_destroy(dev->d_task_q);
14391816cb70SBarry Harding 		dev->d_task_q = NULL;
14401816cb70SBarry Harding 	}
14411816cb70SBarry Harding 
14421816cb70SBarry Harding 	cv_destroy(&dev->d_task_cv);
14431816cb70SBarry Harding 
14441816cb70SBarry Harding 	yge_intr_disable(dev);
14451816cb70SBarry Harding 
14461816cb70SBarry Harding 	if (dev->d_intrh != NULL) {
14471816cb70SBarry Harding 		for (int i = 0; i < dev->d_intrcnt; i++) {
14481816cb70SBarry Harding 			(void) ddi_intr_remove_handler(dev->d_intrh[i]);
14491816cb70SBarry Harding 			(void) ddi_intr_free(dev->d_intrh[i]);
14501816cb70SBarry Harding 		}
14511816cb70SBarry Harding 		kmem_free(dev->d_intrh, dev->d_intrsize);
14521816cb70SBarry Harding 		mutex_destroy(&dev->d_phylock);
14531816cb70SBarry Harding 		mutex_destroy(&dev->d_txlock);
14541816cb70SBarry Harding 		mutex_destroy(&dev->d_rxlock);
14551816cb70SBarry Harding 		mutex_destroy(&dev->d_task_mtx);
14561816cb70SBarry Harding 	}
14571816cb70SBarry Harding 	if (dev->d_regsh != NULL)
14581816cb70SBarry Harding 		ddi_regs_map_free(&dev->d_regsh);
14591816cb70SBarry Harding 
14601816cb70SBarry Harding 	if (dev->d_pcih != NULL)
14611816cb70SBarry Harding 		pci_config_teardown(&dev->d_pcih);
14621816cb70SBarry Harding }
14631816cb70SBarry Harding 
14641816cb70SBarry Harding static int
14651816cb70SBarry Harding yge_alloc_ring(yge_port_t *port, yge_dev_t *dev, yge_ring_t *ring, uint32_t num)
14661816cb70SBarry Harding {
14671816cb70SBarry Harding 	dev_info_t		*dip;
14681816cb70SBarry Harding 	caddr_t			kaddr;
14691816cb70SBarry Harding 	size_t			len;
14701816cb70SBarry Harding 	int			rv;
14711816cb70SBarry Harding 	ddi_dma_cookie_t	dmac;
14721816cb70SBarry Harding 	unsigned		ndmac;
14731816cb70SBarry Harding 
14741816cb70SBarry Harding 	if (port && !dev)
14751816cb70SBarry Harding 		dev = port->p_dev;
14761816cb70SBarry Harding 	dip = dev->d_dip;
14771816cb70SBarry Harding 
14781816cb70SBarry Harding 	ring->r_num = num;
14791816cb70SBarry Harding 
14801816cb70SBarry Harding 	rv = ddi_dma_alloc_handle(dip, &yge_ring_dma_attr, DDI_DMA_DONTWAIT,
14811816cb70SBarry Harding 	    NULL, &ring->r_dmah);
14821816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
14831816cb70SBarry Harding 		yge_error(dev, port, "Unable to allocate ring DMA handle");
14841816cb70SBarry Harding 		return (DDI_FAILURE);
14851816cb70SBarry Harding 	}
14861816cb70SBarry Harding 
14871816cb70SBarry Harding 	rv = ddi_dma_mem_alloc(ring->r_dmah, num * sizeof (yge_desc_t),
14881816cb70SBarry Harding 	    &yge_ring_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
14891816cb70SBarry Harding 	    &kaddr, &len, &ring->r_acch);
14901816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
14911816cb70SBarry Harding 		yge_error(dev, port, "Unable to allocate ring DMA memory");
14921816cb70SBarry Harding 		return (DDI_FAILURE);
14931816cb70SBarry Harding 	}
14941816cb70SBarry Harding 	ring->r_size = len;
14951816cb70SBarry Harding 	ring->r_kaddr = (void *)kaddr;
14961816cb70SBarry Harding 
14971816cb70SBarry Harding 	bzero(kaddr, len);
14981816cb70SBarry Harding 
14991816cb70SBarry Harding 	rv = ddi_dma_addr_bind_handle(ring->r_dmah, NULL, kaddr,
15001816cb70SBarry Harding 	    len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
15011816cb70SBarry Harding 	    &dmac, &ndmac);
15021816cb70SBarry Harding 	if (rv != DDI_DMA_MAPPED) {
15031816cb70SBarry Harding 		yge_error(dev, port, "Unable to bind ring DMA handle");
15041816cb70SBarry Harding 		return (DDI_FAILURE);
15051816cb70SBarry Harding 	}
15061816cb70SBarry Harding 	ASSERT(ndmac == 1);
15071816cb70SBarry Harding 	ring->r_paddr = dmac.dmac_address;
15081816cb70SBarry Harding 
15091816cb70SBarry Harding 	return (DDI_SUCCESS);
15101816cb70SBarry Harding }
15111816cb70SBarry Harding 
15121816cb70SBarry Harding static void
15131816cb70SBarry Harding yge_free_ring(yge_ring_t *ring)
15141816cb70SBarry Harding {
15151816cb70SBarry Harding 	if (ring->r_paddr)
15161816cb70SBarry Harding 		(void) ddi_dma_unbind_handle(ring->r_dmah);
15171816cb70SBarry Harding 	ring->r_paddr = 0;
15181816cb70SBarry Harding 	if (ring->r_acch)
15191816cb70SBarry Harding 		ddi_dma_mem_free(&ring->r_acch);
15201816cb70SBarry Harding 	ring->r_kaddr = NULL;
15211816cb70SBarry Harding 	ring->r_acch = NULL;
15221816cb70SBarry Harding 	if (ring->r_dmah)
15231816cb70SBarry Harding 		ddi_dma_free_handle(&ring->r_dmah);
15241816cb70SBarry Harding 	ring->r_dmah = NULL;
15251816cb70SBarry Harding }
15261816cb70SBarry Harding 
15271816cb70SBarry Harding static int
15281816cb70SBarry Harding yge_alloc_buf(yge_port_t *port, yge_buf_t *b, size_t bufsz, int flag)
15291816cb70SBarry Harding {
15301816cb70SBarry Harding 	yge_dev_t	*dev = port->p_dev;
15311816cb70SBarry Harding 	size_t		l;
15321816cb70SBarry Harding 	int		sflag;
15331816cb70SBarry Harding 	int 		rv;
15341816cb70SBarry Harding 	ddi_dma_cookie_t	dmac;
15351816cb70SBarry Harding 	unsigned		ndmac;
15361816cb70SBarry Harding 
15371816cb70SBarry Harding 	sflag = flag & (DDI_DMA_STREAMING | DDI_DMA_CONSISTENT);
15381816cb70SBarry Harding 
15391816cb70SBarry Harding 	/* Now allocate Tx buffers. */
15401816cb70SBarry Harding 	rv = ddi_dma_alloc_handle(dev->d_dip, &yge_buf_dma_attr,
15411816cb70SBarry Harding 	    DDI_DMA_DONTWAIT, NULL, &b->b_dmah);
15421816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
15431816cb70SBarry Harding 		yge_error(NULL, port, "Unable to alloc DMA handle for buffer");
15441816cb70SBarry Harding 		return (DDI_FAILURE);
15451816cb70SBarry Harding 	}
15461816cb70SBarry Harding 
15471816cb70SBarry Harding 	rv = ddi_dma_mem_alloc(b->b_dmah, bufsz, &yge_buf_attr,
15481816cb70SBarry Harding 	    sflag, DDI_DMA_DONTWAIT, NULL, &b->b_buf, &l, &b->b_acch);
15491816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
15501816cb70SBarry Harding 		yge_error(NULL, port, "Unable to alloc DMA memory for buffer");
15511816cb70SBarry Harding 		return (DDI_FAILURE);
15521816cb70SBarry Harding 	}
15531816cb70SBarry Harding 
15541816cb70SBarry Harding 	rv = ddi_dma_addr_bind_handle(b->b_dmah, NULL, b->b_buf, l, flag,
15551816cb70SBarry Harding 	    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
15561816cb70SBarry Harding 	if (rv != DDI_DMA_MAPPED) {
15571816cb70SBarry Harding 		yge_error(NULL, port, "Unable to bind DMA handle for buffer");
15581816cb70SBarry Harding 		return (DDI_FAILURE);
15591816cb70SBarry Harding 	}
15601816cb70SBarry Harding 	ASSERT(ndmac == 1);
15611816cb70SBarry Harding 	b->b_paddr = dmac.dmac_address;
15621816cb70SBarry Harding 	return (DDI_SUCCESS);
15631816cb70SBarry Harding }
15641816cb70SBarry Harding 
15651816cb70SBarry Harding static void
15661816cb70SBarry Harding yge_free_buf(yge_buf_t *b)
15671816cb70SBarry Harding {
15681816cb70SBarry Harding 	if (b->b_paddr)
15691816cb70SBarry Harding 		(void) ddi_dma_unbind_handle(b->b_dmah);
15701816cb70SBarry Harding 	b->b_paddr = 0;
15711816cb70SBarry Harding 	if (b->b_acch)
15721816cb70SBarry Harding 		ddi_dma_mem_free(&b->b_acch);
15731816cb70SBarry Harding 	b->b_buf = NULL;
15741816cb70SBarry Harding 	b->b_acch = NULL;
15751816cb70SBarry Harding 	if (b->b_dmah)
15761816cb70SBarry Harding 		ddi_dma_free_handle(&b->b_dmah);
15771816cb70SBarry Harding 	b->b_dmah = NULL;
15781816cb70SBarry Harding }
15791816cb70SBarry Harding 
15801816cb70SBarry Harding static int
15811816cb70SBarry Harding yge_txrx_dma_alloc(yge_port_t *port)
15821816cb70SBarry Harding {
15831816cb70SBarry Harding 	uint32_t		bufsz;
15841816cb70SBarry Harding 	int			rv;
15851816cb70SBarry Harding 	int			i;
15861816cb70SBarry Harding 	yge_buf_t		*b;
15871816cb70SBarry Harding 
15881816cb70SBarry Harding 	/*
15891816cb70SBarry Harding 	 * It seems that Yukon II supports full 64 bit DMA operations.
15901816cb70SBarry Harding 	 * But we limit it to 32 bits only for now.  The 64 bit
15911816cb70SBarry Harding 	 * operation would require substantially more complex
15921816cb70SBarry Harding 	 * descriptor handling, since in such a case we would need two
15931816cb70SBarry Harding 	 * LEs to represent a single physical address.
15941816cb70SBarry Harding 	 *
15951816cb70SBarry Harding 	 * If we find that this is limiting us, then we should go back
15961816cb70SBarry Harding 	 * and re-examine it.
15971816cb70SBarry Harding 	 */
15981816cb70SBarry Harding 
15991816cb70SBarry Harding 	/* Note our preferred buffer size. */
16001816cb70SBarry Harding 	bufsz = port->p_mtu;
16011816cb70SBarry Harding 
16021816cb70SBarry Harding 	/* Allocate Tx ring. */
16031816cb70SBarry Harding 	rv = yge_alloc_ring(port, NULL, &port->p_tx_ring, YGE_TX_RING_CNT);
16041816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
16051816cb70SBarry Harding 		return (DDI_FAILURE);
16061816cb70SBarry Harding 	}
16071816cb70SBarry Harding 
16081816cb70SBarry Harding 	/* Now allocate Tx buffers. */
16091816cb70SBarry Harding 	b = port->p_tx_buf;
16101816cb70SBarry Harding 	for (i = 0; i < YGE_TX_RING_CNT; i++) {
16111816cb70SBarry Harding 		rv = yge_alloc_buf(port, b, bufsz,
16121816cb70SBarry Harding 		    DDI_DMA_STREAMING | DDI_DMA_WRITE);
16131816cb70SBarry Harding 		if (rv != DDI_SUCCESS) {
16141816cb70SBarry Harding 			return (DDI_FAILURE);
16151816cb70SBarry Harding 		}
16161816cb70SBarry Harding 		b++;
16171816cb70SBarry Harding 	}
16181816cb70SBarry Harding 
16191816cb70SBarry Harding 	/* Allocate Rx ring. */
16201816cb70SBarry Harding 	rv = yge_alloc_ring(port, NULL, &port->p_rx_ring, YGE_RX_RING_CNT);
16211816cb70SBarry Harding 	if (rv != DDI_SUCCESS) {
16221816cb70SBarry Harding 		return (DDI_FAILURE);
16231816cb70SBarry Harding 	}
16241816cb70SBarry Harding 
16251816cb70SBarry Harding 	/* Now allocate Rx buffers. */
16261816cb70SBarry Harding 	b = port->p_rx_buf;
16271816cb70SBarry Harding 	for (i = 0; i < YGE_RX_RING_CNT; i++) {
16281816cb70SBarry Harding 		rv =  yge_alloc_buf(port, b, bufsz,
16291816cb70SBarry Harding 		    DDI_DMA_STREAMING | DDI_DMA_READ);
16301816cb70SBarry Harding 		if (rv != DDI_SUCCESS) {
16311816cb70SBarry Harding 			return (DDI_FAILURE);
16321816cb70SBarry Harding 		}
16331816cb70SBarry Harding 		b++;
16341816cb70SBarry Harding 	}
16351816cb70SBarry Harding 
16361816cb70SBarry Harding 	return (DDI_SUCCESS);
16371816cb70SBarry Harding }
16381816cb70SBarry Harding 
16391816cb70SBarry Harding static void
16401816cb70SBarry Harding yge_txrx_dma_free(yge_port_t *port)
16411816cb70SBarry Harding {
16421816cb70SBarry Harding 	yge_buf_t	*b;
16431816cb70SBarry Harding 
16441816cb70SBarry Harding 	/* Tx ring. */
16451816cb70SBarry Harding 	yge_free_ring(&port->p_tx_ring);
16461816cb70SBarry Harding 
16471816cb70SBarry Harding 	/* Rx ring. */
16481816cb70SBarry Harding 	yge_free_ring(&port->p_rx_ring);
16491816cb70SBarry Harding 
16501816cb70SBarry Harding 	/* Tx buffers. */
16511816cb70SBarry Harding 	b = port->p_tx_buf;
16521816cb70SBarry Harding 	for (int i = 0; i < YGE_TX_RING_CNT; i++, b++) {
16531816cb70SBarry Harding 		yge_free_buf(b);
16541816cb70SBarry Harding 	}
16551816cb70SBarry Harding 	/* Rx buffers. */
16561816cb70SBarry Harding 	b = port->p_rx_buf;
16571816cb70SBarry Harding 	for (int i = 0; i < YGE_RX_RING_CNT; i++, b++) {
16581816cb70SBarry Harding 		yge_free_buf(b);
16591816cb70SBarry Harding 	}
16601816cb70SBarry Harding }
16611816cb70SBarry Harding 
16621816cb70SBarry Harding boolean_t
16631816cb70SBarry Harding yge_send(yge_port_t *port, mblk_t *mp)
16641816cb70SBarry Harding {
16651816cb70SBarry Harding 	yge_ring_t *ring = &port->p_tx_ring;
16661816cb70SBarry Harding 	yge_buf_t *txb;
16671816cb70SBarry Harding 	int16_t prod;
16681816cb70SBarry Harding 	size_t len;
16691816cb70SBarry Harding 
16701816cb70SBarry Harding 	/*
16711816cb70SBarry Harding 	 * For now we're not going to support checksum offload or LSO.
16721816cb70SBarry Harding 	 */
16731816cb70SBarry Harding 
16741816cb70SBarry Harding 	len = msgsize(mp);
16751816cb70SBarry Harding 	if (len > port->p_framesize) {
16761816cb70SBarry Harding 		/* too big! */
16771816cb70SBarry Harding 		freemsg(mp);
16781816cb70SBarry Harding 		return (B_TRUE);
16791816cb70SBarry Harding 	}
16801816cb70SBarry Harding 
16811816cb70SBarry Harding 	/* Check number of available descriptors. */
16821816cb70SBarry Harding 	if (port->p_tx_cnt + 1 >=
16831816cb70SBarry Harding 	    (YGE_TX_RING_CNT - YGE_RESERVED_TX_DESC_CNT)) {
16841816cb70SBarry Harding 		port->p_wantw = B_TRUE;
16851816cb70SBarry Harding 		return (B_FALSE);
16861816cb70SBarry Harding 	}
16871816cb70SBarry Harding 
16881816cb70SBarry Harding 	prod = port->p_tx_prod;
16891816cb70SBarry Harding 
16901816cb70SBarry Harding 	txb = &port->p_tx_buf[prod];
16911816cb70SBarry Harding 	mcopymsg(mp, txb->b_buf);
16921816cb70SBarry Harding 	SYNCBUF(txb, DDI_DMA_SYNC_FORDEV);
16931816cb70SBarry Harding 
16941816cb70SBarry Harding 	PUTADDR(ring, prod, txb->b_paddr);
16951816cb70SBarry Harding 	PUTCTRL(ring, prod, len | OP_PACKET | HW_OWNER | EOP);
16961816cb70SBarry Harding 	SYNCENTRY(ring, prod, DDI_DMA_SYNC_FORDEV);
16971816cb70SBarry Harding 	port->p_tx_cnt++;
16981816cb70SBarry Harding 
16991816cb70SBarry Harding 	YGE_INC(prod, YGE_TX_RING_CNT);
17001816cb70SBarry Harding 
17011816cb70SBarry Harding 	/* Update producer index. */
17021816cb70SBarry Harding 	port->p_tx_prod = prod;
17031816cb70SBarry Harding 
17041816cb70SBarry Harding 	return (B_TRUE);
17051816cb70SBarry Harding }
17061816cb70SBarry Harding 
17071816cb70SBarry Harding static int
17081816cb70SBarry Harding yge_suspend(yge_dev_t *dev)
17091816cb70SBarry Harding {
17101816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
17111816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
17121816cb70SBarry Harding 		mii_suspend(port->p_mii);
17131816cb70SBarry Harding 	}
17141816cb70SBarry Harding 
17151816cb70SBarry Harding 
17161816cb70SBarry Harding 	DEV_LOCK(dev);
17171816cb70SBarry Harding 
17181816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
17191816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
17201816cb70SBarry Harding 
17211816cb70SBarry Harding 		if (port->p_running) {
17221816cb70SBarry Harding 			yge_stop_port(port);
17231816cb70SBarry Harding 		}
17241816cb70SBarry Harding 	}
17251816cb70SBarry Harding 
17261816cb70SBarry Harding 	/* Disable all interrupts. */
17271816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, 0);
17281816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
17291816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
17301816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
17311816cb70SBarry Harding 
17321816cb70SBarry Harding 	yge_phy_power(dev, B_FALSE);
17331816cb70SBarry Harding 
17341816cb70SBarry Harding 	/* Put hardware reset. */
17351816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
17361816cb70SBarry Harding 	dev->d_suspended = B_TRUE;
17371816cb70SBarry Harding 
17381816cb70SBarry Harding 	DEV_UNLOCK(dev);
17391816cb70SBarry Harding 
17401816cb70SBarry Harding 	return (DDI_SUCCESS);
17411816cb70SBarry Harding }
17421816cb70SBarry Harding 
17431816cb70SBarry Harding static int
17441816cb70SBarry Harding yge_resume(yge_dev_t *dev)
17451816cb70SBarry Harding {
17461816cb70SBarry Harding 	uint8_t pm_cap;
17471816cb70SBarry Harding 
17481816cb70SBarry Harding 	DEV_LOCK(dev);
17491816cb70SBarry Harding 
17501816cb70SBarry Harding 	/* ensure the pmcsr status is D0 state */
17511816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
17521816cb70SBarry Harding 
17531816cb70SBarry Harding 	if ((pm_cap = yge_find_capability(dev, PCI_CAP_ID_PM)) != 0) {
17541816cb70SBarry Harding 		uint16_t pmcsr;
17551816cb70SBarry Harding 		pmcsr = pci_config_get16(dev->d_pcih, pm_cap + PCI_PMCSR);
17561816cb70SBarry Harding 		pmcsr &= ~PCI_PMCSR_STATE_MASK;
17571816cb70SBarry Harding 		pci_config_put16(dev->d_pcih, pm_cap + PCI_PMCSR,
17581816cb70SBarry Harding 		    pmcsr | PCI_PMCSR_D0);
17591816cb70SBarry Harding 	}
17601816cb70SBarry Harding 
17611816cb70SBarry Harding 	/* Enable PCI access and bus master. */
17621816cb70SBarry Harding 	pci_config_put16(dev->d_pcih, PCI_CONF_COMM,
17631816cb70SBarry Harding 	    pci_config_get16(dev->d_pcih, PCI_CONF_COMM) |
17641816cb70SBarry Harding 	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
17651816cb70SBarry Harding 
17661816cb70SBarry Harding 	/* Enable all clocks. */
17671816cb70SBarry Harding 	switch (dev->d_hw_id) {
17681816cb70SBarry Harding 	case CHIP_ID_YUKON_EX:
17691816cb70SBarry Harding 	case CHIP_ID_YUKON_EC_U:
17701816cb70SBarry Harding 	case CHIP_ID_YUKON_FE_P:
17711816cb70SBarry Harding 		pci_config_put32(dev->d_pcih, PCI_OUR_REG_3, 0);
17721816cb70SBarry Harding 		break;
17731816cb70SBarry Harding 	}
17741816cb70SBarry Harding 
17751816cb70SBarry Harding 	CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
17761816cb70SBarry Harding 
17771816cb70SBarry Harding 	yge_reset(dev);
17781816cb70SBarry Harding 
17791816cb70SBarry Harding 	/* Make sure interrupts are reenabled */
17801816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, 0);
17811816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, Y2_IS_HW_ERR | Y2_IS_STAT_BMU);
17821816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK,
17831816cb70SBarry Harding 	    Y2_IS_TIST_OV | Y2_IS_MST_ERR |
17841816cb70SBarry Harding 	    Y2_IS_IRQ_STAT | Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP);
17851816cb70SBarry Harding 
17861816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
17871816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
17881816cb70SBarry Harding 
17891816cb70SBarry Harding 		if (port != NULL && port->p_running) {
17901816cb70SBarry Harding 			yge_start_port(port);
17911816cb70SBarry Harding 		}
17921816cb70SBarry Harding 	}
17931816cb70SBarry Harding 	dev->d_suspended = B_FALSE;
17941816cb70SBarry Harding 
17951816cb70SBarry Harding 	DEV_UNLOCK(dev);
17961816cb70SBarry Harding 
17971816cb70SBarry Harding 	/* Reset MII layer */
17981816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
17991816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
18001816cb70SBarry Harding 
18011816cb70SBarry Harding 		if (port->p_running) {
18021816cb70SBarry Harding 			mii_resume(port->p_mii);
18031816cb70SBarry Harding 			mac_tx_update(port->p_mh);
18041816cb70SBarry Harding 		}
18051816cb70SBarry Harding 	}
18061816cb70SBarry Harding 
18071816cb70SBarry Harding 	return (DDI_SUCCESS);
18081816cb70SBarry Harding }
18091816cb70SBarry Harding 
18101816cb70SBarry Harding static mblk_t *
18111816cb70SBarry Harding yge_rxeof(yge_port_t *port, uint32_t status, int len)
18121816cb70SBarry Harding {
18131816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
18141816cb70SBarry Harding 	mblk_t	*mp;
18151816cb70SBarry Harding 	int cons, rxlen;
18161816cb70SBarry Harding 	yge_buf_t *rxb;
18171816cb70SBarry Harding 	yge_ring_t *ring;
18181816cb70SBarry Harding 
18191816cb70SBarry Harding 	ASSERT(mutex_owned(&dev->d_rxlock));
18201816cb70SBarry Harding 
18211816cb70SBarry Harding 	if (!port->p_running)
18221816cb70SBarry Harding 		return (NULL);
18231816cb70SBarry Harding 
18241816cb70SBarry Harding 	ring = &port->p_rx_ring;
18251816cb70SBarry Harding 	cons = port->p_rx_cons;
18261816cb70SBarry Harding 	rxlen = status >> 16;
18271816cb70SBarry Harding 	rxb = &port->p_rx_buf[cons];
18281816cb70SBarry Harding 	mp = NULL;
18291816cb70SBarry Harding 
18301816cb70SBarry Harding 
18311816cb70SBarry Harding 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
18321816cb70SBarry Harding 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
18331816cb70SBarry Harding 		/*
18341816cb70SBarry Harding 		 * Apparently the status for this chip is not reliable.
18351816cb70SBarry Harding 		 * Only perform minimal consistency checking; the MAC
18361816cb70SBarry Harding 		 * and upper protocols will have to filter any garbage.
18371816cb70SBarry Harding 		 */
18381816cb70SBarry Harding 		if ((len > port->p_framesize) || (rxlen != len)) {
18391816cb70SBarry Harding 			goto bad;
18401816cb70SBarry Harding 		}
18411816cb70SBarry Harding 	} else {
18421816cb70SBarry Harding 		if ((len > port->p_framesize) || (rxlen != len) ||
18431816cb70SBarry Harding 		    ((status & GMR_FS_ANY_ERR) != 0) ||
18441816cb70SBarry Harding 		    ((status & GMR_FS_RX_OK) == 0)) {
18451816cb70SBarry Harding 			goto bad;
18461816cb70SBarry Harding 		}
18471816cb70SBarry Harding 	}
18481816cb70SBarry Harding 
18491816cb70SBarry Harding 	if ((mp = allocb(len + YGE_HEADROOM, BPRI_HI)) != NULL) {
18501816cb70SBarry Harding 
18511816cb70SBarry Harding 		/* good packet - yay */
18521816cb70SBarry Harding 		mp->b_rptr += YGE_HEADROOM;
18531816cb70SBarry Harding 		SYNCBUF(rxb, DDI_DMA_SYNC_FORKERNEL);
18541816cb70SBarry Harding 		bcopy(rxb->b_buf, mp->b_rptr, len);
18551816cb70SBarry Harding 		mp->b_wptr = mp->b_rptr + len;
18561816cb70SBarry Harding 	} else {
18571816cb70SBarry Harding 		port->p_stats.rx_nobuf++;
18581816cb70SBarry Harding 	}
18591816cb70SBarry Harding 
18601816cb70SBarry Harding bad:
18611816cb70SBarry Harding 
18621816cb70SBarry Harding 	PUTCTRL(ring, cons, port->p_framesize | OP_PACKET | HW_OWNER);
18631816cb70SBarry Harding 	SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORDEV);
18641816cb70SBarry Harding 
18651816cb70SBarry Harding 	CSR_WRITE_2(dev,
18661816cb70SBarry Harding 	    Y2_PREF_Q_ADDR(port->p_rxq, PREF_UNIT_PUT_IDX_REG),
18671816cb70SBarry Harding 	    cons);
18681816cb70SBarry Harding 
18691816cb70SBarry Harding 	YGE_INC(port->p_rx_cons, YGE_RX_RING_CNT);
18701816cb70SBarry Harding 
18711816cb70SBarry Harding 	return (mp);
18721816cb70SBarry Harding }
18731816cb70SBarry Harding 
18741816cb70SBarry Harding static boolean_t
18751816cb70SBarry Harding yge_txeof_locked(yge_port_t *port, int idx)
18761816cb70SBarry Harding {
18771816cb70SBarry Harding 	int prog;
18781816cb70SBarry Harding 	int16_t cons;
18791816cb70SBarry Harding 	boolean_t resched;
18801816cb70SBarry Harding 
18811816cb70SBarry Harding 	if (!port->p_running) {
18821816cb70SBarry Harding 		return (B_FALSE);
18831816cb70SBarry Harding 	}
18841816cb70SBarry Harding 
18851816cb70SBarry Harding 	cons = port->p_tx_cons;
18861816cb70SBarry Harding 	prog = 0;
18871816cb70SBarry Harding 	for (; cons != idx; YGE_INC(cons, YGE_TX_RING_CNT)) {
18881816cb70SBarry Harding 		if (port->p_tx_cnt <= 0)
18891816cb70SBarry Harding 			break;
18901816cb70SBarry Harding 		prog++;
18911816cb70SBarry Harding 		port->p_tx_cnt--;
18921816cb70SBarry Harding 		/* No need to sync LEs as we didn't update LEs. */
18931816cb70SBarry Harding 	}
18941816cb70SBarry Harding 
18951816cb70SBarry Harding 	port->p_tx_cons = cons;
18961816cb70SBarry Harding 
18971816cb70SBarry Harding 	if (prog > 0) {
18981816cb70SBarry Harding 		resched = port->p_wantw;
18991816cb70SBarry Harding 		port->p_tx_wdog = 0;
19001816cb70SBarry Harding 		port->p_wantw = B_FALSE;
19011816cb70SBarry Harding 		return (resched);
19021816cb70SBarry Harding 	} else {
19031816cb70SBarry Harding 		return (B_FALSE);
19041816cb70SBarry Harding 	}
19051816cb70SBarry Harding }
19061816cb70SBarry Harding 
19071816cb70SBarry Harding static void
19081816cb70SBarry Harding yge_txeof(yge_port_t *port, int idx)
19091816cb70SBarry Harding {
19101816cb70SBarry Harding 	boolean_t resched;
19111816cb70SBarry Harding 
19121816cb70SBarry Harding 	TX_LOCK(port->p_dev);
19131816cb70SBarry Harding 
19141816cb70SBarry Harding 	resched = yge_txeof_locked(port, idx);
19151816cb70SBarry Harding 
19161816cb70SBarry Harding 	TX_UNLOCK(port->p_dev);
19171816cb70SBarry Harding 
19181816cb70SBarry Harding 	if (resched && port->p_running) {
19191816cb70SBarry Harding 		mac_tx_update(port->p_mh);
19201816cb70SBarry Harding 	}
19211816cb70SBarry Harding }
19221816cb70SBarry Harding 
19231816cb70SBarry Harding static void
19241816cb70SBarry Harding yge_restart_task(yge_dev_t *dev)
19251816cb70SBarry Harding {
19261816cb70SBarry Harding 	yge_port_t *port;
19271816cb70SBarry Harding 
19281816cb70SBarry Harding 	DEV_LOCK(dev);
19291816cb70SBarry Harding 
19301816cb70SBarry Harding 	/* Cancel pending I/O and free all Rx/Tx buffers. */
19311816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
19321816cb70SBarry Harding 		port = dev->d_port[i];
19331816cb70SBarry Harding 		if (port->p_running)
19341816cb70SBarry Harding 			yge_stop_port(dev->d_port[i]);
19351816cb70SBarry Harding 	}
19361816cb70SBarry Harding 	yge_reset(dev);
19371816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
19381816cb70SBarry Harding 		port = dev->d_port[i];
19391816cb70SBarry Harding 
19401816cb70SBarry Harding 		if (port->p_running)
19411816cb70SBarry Harding 			yge_start_port(port);
19421816cb70SBarry Harding 	}
19431816cb70SBarry Harding 
19441816cb70SBarry Harding 	DEV_UNLOCK(dev);
19451816cb70SBarry Harding 
19461816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
19471816cb70SBarry Harding 		port = dev->d_port[i];
19481816cb70SBarry Harding 
19491816cb70SBarry Harding 		mii_reset(port->p_mii);
19501816cb70SBarry Harding 		if (port->p_running)
19511816cb70SBarry Harding 			mac_tx_update(port->p_mh);
19521816cb70SBarry Harding 	}
19531816cb70SBarry Harding }
19541816cb70SBarry Harding 
19551816cb70SBarry Harding static void
19561816cb70SBarry Harding yge_tick(void *arg)
19571816cb70SBarry Harding {
19581816cb70SBarry Harding 	yge_dev_t *dev = arg;
19591816cb70SBarry Harding 	yge_port_t *port;
19601816cb70SBarry Harding 	boolean_t restart = B_FALSE;
19611816cb70SBarry Harding 	boolean_t resched = B_FALSE;
19621816cb70SBarry Harding 	int idx;
19631816cb70SBarry Harding 
19641816cb70SBarry Harding 	DEV_LOCK(dev);
19651816cb70SBarry Harding 
19661816cb70SBarry Harding 	if (dev->d_suspended) {
19671816cb70SBarry Harding 		DEV_UNLOCK(dev);
19681816cb70SBarry Harding 		return;
19691816cb70SBarry Harding 	}
19701816cb70SBarry Harding 
19711816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
19721816cb70SBarry Harding 		port = dev->d_port[i];
19731816cb70SBarry Harding 
19741816cb70SBarry Harding 		if (!port->p_running)
19751816cb70SBarry Harding 			continue;
19761816cb70SBarry Harding 
19771816cb70SBarry Harding 		if (port->p_tx_cnt) {
19781816cb70SBarry Harding 			uint32_t ridx;
19791816cb70SBarry Harding 
19801816cb70SBarry Harding 			/*
19811816cb70SBarry Harding 			 * Reclaim first as there is a possibility of losing
19821816cb70SBarry Harding 			 * Tx completion interrupts.
19831816cb70SBarry Harding 			 */
19841816cb70SBarry Harding 			ridx = port->p_port == YGE_PORT_A ?
19851816cb70SBarry Harding 			    STAT_TXA1_RIDX : STAT_TXA2_RIDX;
19861816cb70SBarry Harding 			idx = CSR_READ_2(dev, ridx);
19871816cb70SBarry Harding 			if (port->p_tx_cons != idx) {
19881816cb70SBarry Harding 				resched = yge_txeof_locked(port, idx);
19891816cb70SBarry Harding 
19901816cb70SBarry Harding 			} else {
19911816cb70SBarry Harding 
19921816cb70SBarry Harding 				/* detect TX hang */
19931816cb70SBarry Harding 				port->p_tx_wdog++;
19941816cb70SBarry Harding 				if (port->p_tx_wdog > YGE_TX_TIMEOUT) {
19951816cb70SBarry Harding 					port->p_tx_wdog = 0;
19961816cb70SBarry Harding 					yge_error(NULL, port,
19971816cb70SBarry Harding 					    "TX hang detected!");
19981816cb70SBarry Harding 					restart = B_TRUE;
19991816cb70SBarry Harding 				}
20001816cb70SBarry Harding 			}
20011816cb70SBarry Harding 		}
20021816cb70SBarry Harding 	}
20031816cb70SBarry Harding 
20041816cb70SBarry Harding 	DEV_UNLOCK(dev);
20051816cb70SBarry Harding 	if (restart) {
20061816cb70SBarry Harding 		yge_dispatch(dev, YGE_TASK_RESTART);
20071816cb70SBarry Harding 	} else {
20081816cb70SBarry Harding 		if (resched) {
20091816cb70SBarry Harding 			for (int i = 0; i < dev->d_num_port; i++) {
20101816cb70SBarry Harding 				port = dev->d_port[i];
20111816cb70SBarry Harding 
20121816cb70SBarry Harding 				if (port->p_running)
20131816cb70SBarry Harding 					mac_tx_update(port->p_mh);
20141816cb70SBarry Harding 			}
20151816cb70SBarry Harding 		}
20161816cb70SBarry Harding 	}
20171816cb70SBarry Harding }
20181816cb70SBarry Harding 
20191816cb70SBarry Harding static int
20201816cb70SBarry Harding yge_intr_gmac(yge_port_t *port)
20211816cb70SBarry Harding {
20221816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
20231816cb70SBarry Harding 	int pnum = port->p_port;
20241816cb70SBarry Harding 	uint8_t status;
20251816cb70SBarry Harding 	int dispatch_wrk = 0;
20261816cb70SBarry Harding 
20271816cb70SBarry Harding 	status = CSR_READ_1(dev, MR_ADDR(pnum, GMAC_IRQ_SRC));
20281816cb70SBarry Harding 
20291816cb70SBarry Harding 	/* GMAC Rx FIFO overrun. */
20301816cb70SBarry Harding 	if ((status & GM_IS_RX_FF_OR) != 0) {
20311816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
20321816cb70SBarry Harding 		yge_error(NULL, port, "Rx FIFO overrun!");
20331816cb70SBarry Harding 		dispatch_wrk |= YGE_TASK_RESTART;
20341816cb70SBarry Harding 	}
20351816cb70SBarry Harding 	/* GMAC Tx FIFO underrun. */
20361816cb70SBarry Harding 	if ((status & GM_IS_TX_FF_UR) != 0) {
20371816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
20381816cb70SBarry Harding 		yge_error(NULL, port, "Tx FIFO underrun!");
20391816cb70SBarry Harding 		/*
20401816cb70SBarry Harding 		 * In case of Tx underrun, we may need to flush/reset
20411816cb70SBarry Harding 		 * Tx MAC but that would also require
20421816cb70SBarry Harding 		 * resynchronization with status LEs. Reinitializing
20431816cb70SBarry Harding 		 * status LEs would affect the other port in dual MAC
20441816cb70SBarry Harding 		 * configuration so it should be avoided if we can.
20451816cb70SBarry Harding 		 * Due to lack of documentation it's all vague guess
20461816cb70SBarry Harding 		 * but it needs more investigation.
20471816cb70SBarry Harding 		 */
20481816cb70SBarry Harding 	}
20491816cb70SBarry Harding 	return (dispatch_wrk);
20501816cb70SBarry Harding }
20511816cb70SBarry Harding 
20521816cb70SBarry Harding static void
20531816cb70SBarry Harding yge_handle_hwerr(yge_port_t *port, uint32_t status)
20541816cb70SBarry Harding {
20551816cb70SBarry Harding 	yge_dev_t	*dev = port->p_dev;
20561816cb70SBarry Harding 
20571816cb70SBarry Harding 	if ((status & Y2_IS_PAR_RD1) != 0) {
20581816cb70SBarry Harding 		yge_error(NULL, port, "RAM buffer read parity error");
20591816cb70SBarry Harding 		/* Clear IRQ. */
20601816cb70SBarry Harding 		CSR_WRITE_2(dev, SELECT_RAM_BUFFER(port->p_port, B3_RI_CTRL),
20611816cb70SBarry Harding 		    RI_CLR_RD_PERR);
20621816cb70SBarry Harding 	}
20631816cb70SBarry Harding 	if ((status & Y2_IS_PAR_WR1) != 0) {
20641816cb70SBarry Harding 		yge_error(NULL, port, "RAM buffer write parity error");
20651816cb70SBarry Harding 		/* Clear IRQ. */
20661816cb70SBarry Harding 		CSR_WRITE_2(dev, SELECT_RAM_BUFFER(port->p_port, B3_RI_CTRL),
20671816cb70SBarry Harding 		    RI_CLR_WR_PERR);
20681816cb70SBarry Harding 	}
20691816cb70SBarry Harding 	if ((status & Y2_IS_PAR_MAC1) != 0) {
20701816cb70SBarry Harding 		yge_error(NULL, port, "Tx MAC parity error");
20711816cb70SBarry Harding 		/* Clear IRQ. */
20721816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(port->p_port, TX_GMF_CTRL_T),
20731816cb70SBarry Harding 		    GMF_CLI_TX_PE);
20741816cb70SBarry Harding 	}
20751816cb70SBarry Harding 	if ((status & Y2_IS_PAR_RX1) != 0) {
20761816cb70SBarry Harding 		yge_error(NULL, port, "Rx parity error");
20771816cb70SBarry Harding 		/* Clear IRQ. */
20781816cb70SBarry Harding 		CSR_WRITE_4(dev, Q_ADDR(port->p_rxq, Q_CSR), BMU_CLR_IRQ_PAR);
20791816cb70SBarry Harding 	}
20801816cb70SBarry Harding 	if ((status & (Y2_IS_TCP_TXS1 | Y2_IS_TCP_TXA1)) != 0) {
20811816cb70SBarry Harding 		yge_error(NULL, port, "TCP segmentation error");
20821816cb70SBarry Harding 		/* Clear IRQ. */
20831816cb70SBarry Harding 		CSR_WRITE_4(dev, Q_ADDR(port->p_txq, Q_CSR), BMU_CLR_IRQ_TCP);
20841816cb70SBarry Harding 	}
20851816cb70SBarry Harding }
20861816cb70SBarry Harding 
20871816cb70SBarry Harding static void
20881816cb70SBarry Harding yge_intr_hwerr(yge_dev_t *dev)
20891816cb70SBarry Harding {
20901816cb70SBarry Harding 	uint32_t status;
20911816cb70SBarry Harding 	uint32_t tlphead[4];
20921816cb70SBarry Harding 
20931816cb70SBarry Harding 	status = CSR_READ_4(dev, B0_HWE_ISRC);
20941816cb70SBarry Harding 	/* Time Stamp timer overflow. */
20951816cb70SBarry Harding 	if ((status & Y2_IS_TIST_OV) != 0)
20961816cb70SBarry Harding 		CSR_WRITE_1(dev, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
20971816cb70SBarry Harding 	if ((status & Y2_IS_PCI_NEXP) != 0) {
20981816cb70SBarry Harding 		/*
20991816cb70SBarry Harding 		 * PCI Express Error occurred which is not described in PEX
21001816cb70SBarry Harding 		 * spec.
21011816cb70SBarry Harding 		 * This error is also mapped either to Master Abort(
21021816cb70SBarry Harding 		 * Y2_IS_MST_ERR) or Target Abort (Y2_IS_IRQ_STAT) bit and
21031816cb70SBarry Harding 		 * can only be cleared there.
21041816cb70SBarry Harding 		 */
21051816cb70SBarry Harding 		yge_error(dev, NULL, "PCI Express protocol violation error");
21061816cb70SBarry Harding 	}
21071816cb70SBarry Harding 
21081816cb70SBarry Harding 	if ((status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) != 0) {
21091816cb70SBarry Harding 		uint16_t v16;
21101816cb70SBarry Harding 
21111816cb70SBarry Harding 		if ((status & Y2_IS_IRQ_STAT) != 0)
21121816cb70SBarry Harding 			yge_error(dev, NULL, "Unexpected IRQ Status error");
21131816cb70SBarry Harding 		if ((status & Y2_IS_MST_ERR) != 0)
21141816cb70SBarry Harding 			yge_error(dev, NULL, "Unexpected IRQ Master error");
21151816cb70SBarry Harding 		/* Reset all bits in the PCI status register. */
21161816cb70SBarry Harding 		v16 = pci_config_get16(dev->d_pcih, PCI_CONF_STAT);
21171816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
21181816cb70SBarry Harding 		pci_config_put16(dev->d_pcih, PCI_CONF_STAT, v16 |
21191816cb70SBarry Harding 		    PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB |
21201816cb70SBarry Harding 		    PCI_STAT_R_TARG_AB | PCI_STAT_PERROR);
21211816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
21221816cb70SBarry Harding 	}
21231816cb70SBarry Harding 
21241816cb70SBarry Harding 	/* Check for PCI Express Uncorrectable Error. */
21251816cb70SBarry Harding 	if ((status & Y2_IS_PCI_EXP) != 0) {
21261816cb70SBarry Harding 		uint32_t v32;
21271816cb70SBarry Harding 
21281816cb70SBarry Harding 		/*
21291816cb70SBarry Harding 		 * On PCI Express bus bridges are called root complexes (RC).
21301816cb70SBarry Harding 		 * PCI Express errors are recognized by the root complex too,
21311816cb70SBarry Harding 		 * which requests the system to handle the problem. After
21321816cb70SBarry Harding 		 * error occurrence it may be that no access to the adapter
21331816cb70SBarry Harding 		 * may be performed any longer.
21341816cb70SBarry Harding 		 */
21351816cb70SBarry Harding 
21361816cb70SBarry Harding 		v32 = CSR_PCI_READ_4(dev, PEX_UNC_ERR_STAT);
21371816cb70SBarry Harding 		if ((v32 & PEX_UNSUP_REQ) != 0) {
21381816cb70SBarry Harding 			/* Ignore unsupported request error. */
21391816cb70SBarry Harding 			yge_error(dev, NULL,
21401816cb70SBarry Harding 			    "Uncorrectable PCI Express error");
21411816cb70SBarry Harding 		}
21421816cb70SBarry Harding 		if ((v32 & (PEX_FATAL_ERRORS | PEX_POIS_TLP)) != 0) {
21431816cb70SBarry Harding 			int i;
21441816cb70SBarry Harding 
21451816cb70SBarry Harding 			/* Get TLP header form Log Registers. */
21461816cb70SBarry Harding 			for (i = 0; i < 4; i++)
21471816cb70SBarry Harding 				tlphead[i] = CSR_PCI_READ_4(dev,
21481816cb70SBarry Harding 				    PEX_HEADER_LOG + i * 4);
21491816cb70SBarry Harding 			/* Check for vendor defined broadcast message. */
21501816cb70SBarry Harding 			if (!(tlphead[0] == 0x73004001 && tlphead[1] == 0x7f)) {
21511816cb70SBarry Harding 				dev->d_intrhwemask &= ~Y2_IS_PCI_EXP;
21521816cb70SBarry Harding 				CSR_WRITE_4(dev, B0_HWE_IMSK,
21531816cb70SBarry Harding 				    dev->d_intrhwemask);
21541816cb70SBarry Harding 				(void) CSR_READ_4(dev, B0_HWE_IMSK);
21551816cb70SBarry Harding 			}
21561816cb70SBarry Harding 		}
21571816cb70SBarry Harding 		/* Clear the interrupt. */
21581816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_ON);
21591816cb70SBarry Harding 		CSR_PCI_WRITE_4(dev, PEX_UNC_ERR_STAT, 0xffffffff);
21601816cb70SBarry Harding 		CSR_WRITE_1(dev, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
21611816cb70SBarry Harding 	}
21621816cb70SBarry Harding 
21631816cb70SBarry Harding 	if ((status & Y2_HWE_L1_MASK) != 0 && dev->d_port[YGE_PORT_A] != NULL)
21641816cb70SBarry Harding 		yge_handle_hwerr(dev->d_port[YGE_PORT_A], status);
21651816cb70SBarry Harding 	if ((status & Y2_HWE_L2_MASK) != 0 && dev->d_port[YGE_PORT_B] != NULL)
21661816cb70SBarry Harding 		yge_handle_hwerr(dev->d_port[YGE_PORT_B], status >> 8);
21671816cb70SBarry Harding }
21681816cb70SBarry Harding 
21691816cb70SBarry Harding /*
21701816cb70SBarry Harding  * Returns B_TRUE if there is potentially more work to do.
21711816cb70SBarry Harding  */
21721816cb70SBarry Harding static boolean_t
21731816cb70SBarry Harding yge_handle_events(yge_dev_t *dev, mblk_t **heads, mblk_t **tails, int *txindex)
21741816cb70SBarry Harding {
21751816cb70SBarry Harding 	yge_port_t *port;
21761816cb70SBarry Harding 	yge_ring_t *ring;
21771816cb70SBarry Harding 	uint32_t control, status;
21781816cb70SBarry Harding 	int cons, idx, len, pnum;
21791816cb70SBarry Harding 	mblk_t *mp;
21801816cb70SBarry Harding 	uint32_t rxprogs[2];
21811816cb70SBarry Harding 
21821816cb70SBarry Harding 	rxprogs[0] = rxprogs[1] = 0;
21831816cb70SBarry Harding 
21841816cb70SBarry Harding 	idx = CSR_READ_2(dev, STAT_PUT_IDX);
21851816cb70SBarry Harding 	if (idx == dev->d_stat_cons) {
21861816cb70SBarry Harding 		return (B_FALSE);
21871816cb70SBarry Harding 	}
21881816cb70SBarry Harding 
21891816cb70SBarry Harding 	ring = &dev->d_status_ring;
21901816cb70SBarry Harding 
21911816cb70SBarry Harding 	for (cons = dev->d_stat_cons; cons != idx; ) {
21921816cb70SBarry Harding 		/* Sync status LE. */
21931816cb70SBarry Harding 		SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORKERNEL);
21941816cb70SBarry Harding 		control = GETCTRL(ring, cons);
21951816cb70SBarry Harding 		if ((control & HW_OWNER) == 0) {
21961816cb70SBarry Harding 			yge_error(dev, NULL, "Status descriptor error: "
21971816cb70SBarry Harding 			    "index %d, control %x", cons, control);
21981816cb70SBarry Harding 			break;
21991816cb70SBarry Harding 		}
22001816cb70SBarry Harding 
22011816cb70SBarry Harding 		status = GETSTAT(ring, cons);
22021816cb70SBarry Harding 
22031816cb70SBarry Harding 		control &= ~HW_OWNER;
22041816cb70SBarry Harding 		len = control & STLE_LEN_MASK;
22051816cb70SBarry Harding 		pnum = ((control >> 16) & 0x01);
22061816cb70SBarry Harding 		port = dev->d_port[pnum];
22071816cb70SBarry Harding 		if (port == NULL) {
22081816cb70SBarry Harding 			yge_error(dev, NULL, "Invalid port opcode: 0x%08x",
22091816cb70SBarry Harding 			    control & STLE_OP_MASK);
22101816cb70SBarry Harding 			goto finish;
22111816cb70SBarry Harding 		}
22121816cb70SBarry Harding 
22131816cb70SBarry Harding 		switch (control & STLE_OP_MASK) {
22141816cb70SBarry Harding 		case OP_RXSTAT:
22151816cb70SBarry Harding 			mp = yge_rxeof(port, status, len);
22161816cb70SBarry Harding 			if (mp != NULL) {
22171816cb70SBarry Harding 				if (heads[pnum] == NULL)
22181816cb70SBarry Harding 					heads[pnum] = mp;
22191816cb70SBarry Harding 				else
22201816cb70SBarry Harding 					tails[pnum]->b_next = mp;
22211816cb70SBarry Harding 				tails[pnum] = mp;
22221816cb70SBarry Harding 			}
22231816cb70SBarry Harding 
22241816cb70SBarry Harding 			rxprogs[pnum]++;
22251816cb70SBarry Harding 			break;
22261816cb70SBarry Harding 
22271816cb70SBarry Harding 		case OP_TXINDEXLE:
22281816cb70SBarry Harding 			txindex[0] = status & STLE_TXA1_MSKL;
22291816cb70SBarry Harding 			txindex[1] =
22301816cb70SBarry Harding 			    ((status & STLE_TXA2_MSKL) >> STLE_TXA2_SHIFTL) |
22311816cb70SBarry Harding 			    ((len & STLE_TXA2_MSKH) << STLE_TXA2_SHIFTH);
22321816cb70SBarry Harding 			break;
22331816cb70SBarry Harding 		default:
22341816cb70SBarry Harding 			yge_error(dev, NULL, "Unhandled opcode: 0x%08x",
22351816cb70SBarry Harding 			    control & STLE_OP_MASK);
22361816cb70SBarry Harding 			break;
22371816cb70SBarry Harding 		}
22381816cb70SBarry Harding finish:
22391816cb70SBarry Harding 
22401816cb70SBarry Harding 		/* Give it back to HW. */
22411816cb70SBarry Harding 		PUTCTRL(ring, cons, control);
22421816cb70SBarry Harding 		SYNCENTRY(ring, cons, DDI_DMA_SYNC_FORDEV);
22431816cb70SBarry Harding 
22441816cb70SBarry Harding 		YGE_INC(cons, YGE_STAT_RING_CNT);
22451816cb70SBarry Harding 		if (rxprogs[pnum] > dev->d_process_limit) {
22461816cb70SBarry Harding 			break;
22471816cb70SBarry Harding 		}
22481816cb70SBarry Harding 	}
22491816cb70SBarry Harding 
22501816cb70SBarry Harding 	dev->d_stat_cons = cons;
22511816cb70SBarry Harding 	if (dev->d_stat_cons != CSR_READ_2(dev, STAT_PUT_IDX))
22521816cb70SBarry Harding 		return (B_TRUE);
22531816cb70SBarry Harding 	else
22541816cb70SBarry Harding 		return (B_FALSE);
22551816cb70SBarry Harding }
22561816cb70SBarry Harding 
22571816cb70SBarry Harding /*ARGSUSED1*/
22581816cb70SBarry Harding static uint_t
22591816cb70SBarry Harding yge_intr(caddr_t arg1, caddr_t arg2)
22601816cb70SBarry Harding {
22611816cb70SBarry Harding 	yge_dev_t	*dev;
22621816cb70SBarry Harding 	yge_port_t	*port1;
22631816cb70SBarry Harding 	yge_port_t	*port2;
22641816cb70SBarry Harding 	uint32_t	status;
22651816cb70SBarry Harding 	mblk_t		*heads[2], *tails[2];
22661816cb70SBarry Harding 	int		txindex[2];
22671816cb70SBarry Harding 	int		dispatch_wrk;
22681816cb70SBarry Harding 
22691816cb70SBarry Harding 	dev = (void *)arg1;
22701816cb70SBarry Harding 
22711816cb70SBarry Harding 	heads[0] = heads[1] = NULL;
22721816cb70SBarry Harding 	tails[0] = tails[1] = NULL;
22731816cb70SBarry Harding 	txindex[0] = txindex[1] = -1;
22741816cb70SBarry Harding 	dispatch_wrk = 0;
22751816cb70SBarry Harding 
22761816cb70SBarry Harding 	port1 = dev->d_port[YGE_PORT_A];
22771816cb70SBarry Harding 	port2 = dev->d_port[YGE_PORT_B];
22781816cb70SBarry Harding 
22791816cb70SBarry Harding 	RX_LOCK(dev);
22801816cb70SBarry Harding 
22811816cb70SBarry Harding 	if (dev->d_suspended) {
22821816cb70SBarry Harding 		RX_UNLOCK(dev);
22831816cb70SBarry Harding 		return (DDI_INTR_UNCLAIMED);
22841816cb70SBarry Harding 	}
22851816cb70SBarry Harding 
22861816cb70SBarry Harding 	/* Get interrupt source. */
22871816cb70SBarry Harding 	status = CSR_READ_4(dev, B0_Y2_SP_ISRC2);
22881816cb70SBarry Harding 	if (status == 0 || status == 0xffffffff ||
22891816cb70SBarry Harding 	    (status & dev->d_intrmask) == 0) { /* Stray interrupt ? */
22901816cb70SBarry Harding 		/* Reenable interrupts. */
22911816cb70SBarry Harding 		CSR_WRITE_4(dev, B0_Y2_SP_ICR, 2);
22921816cb70SBarry Harding 		RX_UNLOCK(dev);
22931816cb70SBarry Harding 		return (DDI_INTR_UNCLAIMED);
22941816cb70SBarry Harding 	}
22951816cb70SBarry Harding 
22961816cb70SBarry Harding 	if ((status & Y2_IS_HW_ERR) != 0) {
22971816cb70SBarry Harding 		yge_intr_hwerr(dev);
22981816cb70SBarry Harding 	}
22991816cb70SBarry Harding 
23001816cb70SBarry Harding 	if (status & Y2_IS_IRQ_MAC1) {
23011816cb70SBarry Harding 		dispatch_wrk |= yge_intr_gmac(port1);
23021816cb70SBarry Harding 	}
23031816cb70SBarry Harding 	if (status & Y2_IS_IRQ_MAC2) {
23041816cb70SBarry Harding 		dispatch_wrk |= yge_intr_gmac(port2);
23051816cb70SBarry Harding 	}
23061816cb70SBarry Harding 
23071816cb70SBarry Harding 	if ((status & (Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2)) != 0) {
23081816cb70SBarry Harding 		yge_error(NULL, status & Y2_IS_CHK_RX1 ? port1 : port2,
23091816cb70SBarry Harding 		    "Rx descriptor error");
23101816cb70SBarry Harding 		dev->d_intrmask &= ~(Y2_IS_CHK_RX1 | Y2_IS_CHK_RX2);
23111816cb70SBarry Harding 		CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
23121816cb70SBarry Harding 		(void) CSR_READ_4(dev, B0_IMSK);
23131816cb70SBarry Harding 	}
23141816cb70SBarry Harding 	if ((status & (Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2)) != 0) {
23151816cb70SBarry Harding 		yge_error(NULL, status & Y2_IS_CHK_TXA1 ? port1 : port2,
23161816cb70SBarry Harding 		    "Tx descriptor error");
23171816cb70SBarry Harding 		dev->d_intrmask &= ~(Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXA2);
23181816cb70SBarry Harding 		CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
23191816cb70SBarry Harding 		(void) CSR_READ_4(dev, B0_IMSK);
23201816cb70SBarry Harding 	}
23211816cb70SBarry Harding 
23221816cb70SBarry Harding 	/* handle events until it returns false */
23231816cb70SBarry Harding 	while (yge_handle_events(dev, heads, tails, txindex))
23241816cb70SBarry Harding 		/* NOP */;
23251816cb70SBarry Harding 
23261816cb70SBarry Harding 	/* Do receive/transmit events */
23271816cb70SBarry Harding 	if ((status & Y2_IS_STAT_BMU)) {
23281816cb70SBarry Harding 		CSR_WRITE_4(dev, STAT_CTRL, SC_STAT_CLR_IRQ);
23291816cb70SBarry Harding 	}
23301816cb70SBarry Harding 
23311816cb70SBarry Harding 	/* Reenable interrupts. */
23321816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_Y2_SP_ICR, 2);
23331816cb70SBarry Harding 
23341816cb70SBarry Harding 	RX_UNLOCK(dev);
23351816cb70SBarry Harding 
23361816cb70SBarry Harding 	if (dispatch_wrk) {
23371816cb70SBarry Harding 		yge_dispatch(dev, dispatch_wrk);
23381816cb70SBarry Harding 	}
23391816cb70SBarry Harding 
23401816cb70SBarry Harding 	if (port1->p_running) {
23411816cb70SBarry Harding 		if (txindex[0] >= 0) {
23421816cb70SBarry Harding 			yge_txeof(port1, txindex[0]);
23431816cb70SBarry Harding 		}
23441816cb70SBarry Harding 		if (heads[0])
23451816cb70SBarry Harding 			mac_rx(port1->p_mh, NULL, heads[0]);
23461816cb70SBarry Harding 	} else {
23471816cb70SBarry Harding 		if (heads[0]) {
23481816cb70SBarry Harding 			mblk_t *mp;
23491816cb70SBarry Harding 			while ((mp = heads[0]) != NULL) {
23501816cb70SBarry Harding 				heads[0] = mp->b_next;
23511816cb70SBarry Harding 				freemsg(mp);
23521816cb70SBarry Harding 			}
23531816cb70SBarry Harding 		}
23541816cb70SBarry Harding 	}
23551816cb70SBarry Harding 
23561816cb70SBarry Harding 	if (port2->p_running) {
23571816cb70SBarry Harding 		if (txindex[1] >= 0) {
23581816cb70SBarry Harding 			yge_txeof(port2, txindex[1]);
23591816cb70SBarry Harding 		}
23601816cb70SBarry Harding 		if (heads[1])
23611816cb70SBarry Harding 			mac_rx(port2->p_mh, NULL, heads[1]);
23621816cb70SBarry Harding 	} else {
23631816cb70SBarry Harding 		if (heads[1]) {
23641816cb70SBarry Harding 			mblk_t *mp;
23651816cb70SBarry Harding 			while ((mp = heads[1]) != NULL) {
23661816cb70SBarry Harding 				heads[1] = mp->b_next;
23671816cb70SBarry Harding 				freemsg(mp);
23681816cb70SBarry Harding 			}
23691816cb70SBarry Harding 		}
23701816cb70SBarry Harding 	}
23711816cb70SBarry Harding 
23721816cb70SBarry Harding 	return (DDI_INTR_CLAIMED);
23731816cb70SBarry Harding }
23741816cb70SBarry Harding 
23751816cb70SBarry Harding static void
23761816cb70SBarry Harding yge_set_tx_stfwd(yge_port_t *port)
23771816cb70SBarry Harding {
23781816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
23791816cb70SBarry Harding 	int pnum = port->p_port;
23801816cb70SBarry Harding 
23811816cb70SBarry Harding 	switch (dev->d_hw_id) {
23821816cb70SBarry Harding 	case CHIP_ID_YUKON_EX:
23831816cb70SBarry Harding 		if (dev->d_hw_rev == CHIP_REV_YU_EX_A0)
23841816cb70SBarry Harding 			goto yukon_ex_workaround;
23851816cb70SBarry Harding 
23861816cb70SBarry Harding 		if (port->p_mtu > ETHERMTU)
23871816cb70SBarry Harding 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
23881816cb70SBarry Harding 			    TX_JUMBO_ENA | TX_STFW_ENA);
23891816cb70SBarry Harding 		else
23901816cb70SBarry Harding 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
23911816cb70SBarry Harding 			    TX_JUMBO_DIS | TX_STFW_ENA);
23921816cb70SBarry Harding 		break;
23931816cb70SBarry Harding 	default:
23941816cb70SBarry Harding yukon_ex_workaround:
23951816cb70SBarry Harding 		if (port->p_mtu > ETHERMTU) {
23961816cb70SBarry Harding 			/* Set Tx GMAC FIFO Almost Empty Threshold. */
23971816cb70SBarry Harding 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_AE_THR),
23981816cb70SBarry Harding 			    MSK_ECU_JUMBO_WM << 16 | MSK_ECU_AE_THR);
23991816cb70SBarry Harding 			/* Disable Store & Forward mode for Tx. */
24001816cb70SBarry Harding 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
24011816cb70SBarry Harding 			    TX_JUMBO_ENA | TX_STFW_DIS);
24021816cb70SBarry Harding 		} else {
24031816cb70SBarry Harding 			/* Enable Store & Forward mode for Tx. */
24041816cb70SBarry Harding 			CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T),
24051816cb70SBarry Harding 			    TX_JUMBO_DIS | TX_STFW_ENA);
24061816cb70SBarry Harding 		}
24071816cb70SBarry Harding 		break;
24081816cb70SBarry Harding 	}
24091816cb70SBarry Harding }
24101816cb70SBarry Harding 
24111816cb70SBarry Harding static void
24121816cb70SBarry Harding yge_start_port(yge_port_t *port)
24131816cb70SBarry Harding {
24141816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
24151816cb70SBarry Harding 	uint16_t gmac;
24161816cb70SBarry Harding 	int32_t pnum;
24171816cb70SBarry Harding 	int32_t rxq;
24181816cb70SBarry Harding 	int32_t txq;
24191816cb70SBarry Harding 	uint32_t reg;
24201816cb70SBarry Harding 
24211816cb70SBarry Harding 	pnum = port->p_port;
24221816cb70SBarry Harding 	txq = port->p_txq;
24231816cb70SBarry Harding 	rxq = port->p_rxq;
24241816cb70SBarry Harding 
24251816cb70SBarry Harding 	if (port->p_mtu < ETHERMTU)
24261816cb70SBarry Harding 		port->p_framesize = ETHERMTU;
24271816cb70SBarry Harding 	else
24281816cb70SBarry Harding 		port->p_framesize = port->p_mtu;
24291816cb70SBarry Harding 	port->p_framesize += sizeof (struct ether_vlan_header);
24301816cb70SBarry Harding 
24311816cb70SBarry Harding 	/*
24321816cb70SBarry Harding 	 * Note for the future, if we enable offloads:
24331816cb70SBarry Harding 	 * In Yukon EC Ultra, TSO & checksum offload is not
24341816cb70SBarry Harding 	 * supported for jumbo frame.
24351816cb70SBarry Harding 	 */
24361816cb70SBarry Harding 
24371816cb70SBarry Harding 	/* GMAC Control reset */
24381816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_RST_SET);
24391816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_RST_CLR);
24401816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_F_LOOPB_OFF);
24411816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_EX)
24421816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL),
24431816cb70SBarry Harding 		    GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON |
24441816cb70SBarry Harding 		    GMC_BYP_RETR_ON);
24451816cb70SBarry Harding 	/*
24461816cb70SBarry Harding 	 * Initialize GMAC first such that speed/duplex/flow-control
24471816cb70SBarry Harding 	 * parameters are renegotiated with the interface is brought up.
24481816cb70SBarry Harding 	 */
24491816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_GP_CTRL, 0);
24501816cb70SBarry Harding 
24511816cb70SBarry Harding 	/* Dummy read the Interrupt Source Register. */
24521816cb70SBarry Harding 	(void) CSR_READ_1(dev, MR_ADDR(pnum, GMAC_IRQ_SRC));
24531816cb70SBarry Harding 
24541816cb70SBarry Harding 	/* Clear MIB stats. */
24551816cb70SBarry Harding 	yge_stats_clear(port);
24561816cb70SBarry Harding 
24571816cb70SBarry Harding 	/* Disable FCS. */
24581816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_RX_CTRL, GM_RXCR_CRC_DIS);
24591816cb70SBarry Harding 
24601816cb70SBarry Harding 	/* Setup Transmit Control Register. */
24611816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
24621816cb70SBarry Harding 
24631816cb70SBarry Harding 	/* Setup Transmit Flow Control Register. */
24641816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_TX_FLOW_CTRL, 0xffff);
24651816cb70SBarry Harding 
24661816cb70SBarry Harding 	/* Setup Transmit Parameter Register. */
24671816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_TX_PARAM,
24681816cb70SBarry Harding 	    TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) | TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
24691816cb70SBarry Harding 	    TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) | TX_BACK_OFF_LIM(TX_BOF_LIM_DEF));
24701816cb70SBarry Harding 
24711816cb70SBarry Harding 	gmac = DATA_BLIND_VAL(DATA_BLIND_DEF) |
24721816cb70SBarry Harding 	    GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
24731816cb70SBarry Harding 
24741816cb70SBarry Harding 	if (port->p_mtu > ETHERMTU)
24751816cb70SBarry Harding 		gmac |= GM_SMOD_JUMBO_ENA;
24761816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_SERIAL_MODE, gmac);
24771816cb70SBarry Harding 
24781816cb70SBarry Harding 	/* Disable interrupts for counter overflows. */
24791816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_TX_IRQ_MSK, 0);
24801816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_RX_IRQ_MSK, 0);
24811816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_TR_IRQ_MSK, 0);
24821816cb70SBarry Harding 
24831816cb70SBarry Harding 	/* Configure Rx MAC FIFO. */
24841816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_SET);
24851816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_CLR);
24861816cb70SBarry Harding 	reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
24871816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_FE_P ||
24881816cb70SBarry Harding 	    dev->d_hw_id == CHIP_ID_YUKON_EX)
24891816cb70SBarry Harding 		reg |= GMF_RX_OVER_ON;
24901816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), reg);
24911816cb70SBarry Harding 
24921816cb70SBarry Harding 	/* Set receive filter. */
24931816cb70SBarry Harding 	yge_setrxfilt(port);
24941816cb70SBarry Harding 
24951816cb70SBarry Harding 	/* Flush Rx MAC FIFO on any flow control or error. */
24961816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_FL_MSK), GMR_FS_ANY_ERR);
24971816cb70SBarry Harding 
24981816cb70SBarry Harding 	/*
24991816cb70SBarry Harding 	 * Set Rx FIFO flush threshold to 64 bytes + 1 FIFO word
25001816cb70SBarry Harding 	 * due to hardware hang on receipt of pause frames.
25011816cb70SBarry Harding 	 */
25021816cb70SBarry Harding 	reg = RX_GMF_FL_THR_DEF + 1;
25031816cb70SBarry Harding 	/* FE+ magic */
25041816cb70SBarry Harding 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
25051816cb70SBarry Harding 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0))
25061816cb70SBarry Harding 		reg = 0x178;
25071816cb70SBarry Harding 
25081816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_FL_THR), reg);
25091816cb70SBarry Harding 
25101816cb70SBarry Harding 	/* Configure Tx MAC FIFO. */
25111816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_SET);
25121816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_CLR);
25131816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_OPER_ON);
25141816cb70SBarry Harding 
25151816cb70SBarry Harding 	/* Disable hardware VLAN tag insertion/stripping. */
25161816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
25171816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
25181816cb70SBarry Harding 
25191816cb70SBarry Harding 	if ((port->p_flags & PORT_FLAG_RAMBUF) == 0) {
25201816cb70SBarry Harding 		/* Set Rx Pause threshold. */
25211816cb70SBarry Harding 		if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
25221816cb70SBarry Harding 		    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
25231816cb70SBarry Harding 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_LP_THR),
25241816cb70SBarry Harding 			    MSK_ECU_LLPP);
25251816cb70SBarry Harding 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_UP_THR),
25261816cb70SBarry Harding 			    MSK_FEP_ULPP);
25271816cb70SBarry Harding 		} else {
25281816cb70SBarry Harding 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_LP_THR),
25291816cb70SBarry Harding 			    MSK_ECU_LLPP);
25301816cb70SBarry Harding 			CSR_WRITE_1(dev, MR_ADDR(pnum, RX_GMF_UP_THR),
25311816cb70SBarry Harding 			    MSK_ECU_ULPP);
25321816cb70SBarry Harding 		}
25331816cb70SBarry Harding 		/* Configure store-and-forward for TX */
25341816cb70SBarry Harding 		yge_set_tx_stfwd(port);
25351816cb70SBarry Harding 	}
25361816cb70SBarry Harding 
25371816cb70SBarry Harding 	if ((dev->d_hw_id == CHIP_ID_YUKON_FE_P) &&
25381816cb70SBarry Harding 	    (dev->d_hw_rev == CHIP_REV_YU_FE2_A0)) {
25391816cb70SBarry Harding 		/* Disable dynamic watermark */
25401816cb70SBarry Harding 		reg = CSR_READ_4(dev, MR_ADDR(pnum, TX_GMF_EA));
25411816cb70SBarry Harding 		reg &= ~TX_DYN_WM_ENA;
25421816cb70SBarry Harding 		CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_EA), reg);
25431816cb70SBarry Harding 	}
25441816cb70SBarry Harding 
25451816cb70SBarry Harding 	/*
25461816cb70SBarry Harding 	 * Disable Force Sync bit and Alloc bit in Tx RAM interface
25471816cb70SBarry Harding 	 * arbiter as we don't use Sync Tx queue.
25481816cb70SBarry Harding 	 */
25491816cb70SBarry Harding 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL),
25501816cb70SBarry Harding 	    TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
25511816cb70SBarry Harding 	/* Enable the RAM Interface Arbiter. */
25521816cb70SBarry Harding 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL), TXA_ENA_ARB);
25531816cb70SBarry Harding 
25541816cb70SBarry Harding 	/* Setup RAM buffer. */
25551816cb70SBarry Harding 	yge_set_rambuffer(port);
25561816cb70SBarry Harding 
25571816cb70SBarry Harding 	/* Disable Tx sync Queue. */
25581816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(port->p_txsq, RB_CTRL), RB_RST_SET);
25591816cb70SBarry Harding 
25601816cb70SBarry Harding 	/* Setup Tx Queue Bus Memory Interface. */
25611816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_CLR_RESET);
25621816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_OPER_INIT);
25631816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_FIFO_OP_ON);
25641816cb70SBarry Harding 	CSR_WRITE_2(dev, Q_ADDR(txq, Q_WM), MSK_BMU_TX_WM);
25651816cb70SBarry Harding 
25661816cb70SBarry Harding 	switch (dev->d_hw_id) {
25671816cb70SBarry Harding 	case CHIP_ID_YUKON_EC_U:
25681816cb70SBarry Harding 		if (dev->d_hw_rev == CHIP_REV_YU_EC_U_A0) {
25691816cb70SBarry Harding 			/* Fix for Yukon-EC Ultra: set BMU FIFO level */
25701816cb70SBarry Harding 			CSR_WRITE_2(dev, Q_ADDR(txq, Q_AL), MSK_ECU_TXFF_LEV);
25711816cb70SBarry Harding 		}
25721816cb70SBarry Harding 		break;
25731816cb70SBarry Harding 	case CHIP_ID_YUKON_EX:
25741816cb70SBarry Harding 		/*
25751816cb70SBarry Harding 		 * Yukon Extreme seems to have silicon bug for
25761816cb70SBarry Harding 		 * automatic Tx checksum calculation capability.
25771816cb70SBarry Harding 		 */
25781816cb70SBarry Harding 		if (dev->d_hw_rev == CHIP_REV_YU_EX_B0)
25791816cb70SBarry Harding 			CSR_WRITE_4(dev, Q_ADDR(txq, Q_F), F_TX_CHK_AUTO_OFF);
25801816cb70SBarry Harding 		break;
25811816cb70SBarry Harding 	}
25821816cb70SBarry Harding 
25831816cb70SBarry Harding 	/* Setup Rx Queue Bus Memory Interface. */
25841816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_CLR_RESET);
25851816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_OPER_INIT);
25861816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_FIFO_OP_ON);
25871816cb70SBarry Harding 	if (dev->d_bustype == PEX_BUS) {
25881816cb70SBarry Harding 		CSR_WRITE_2(dev, Q_ADDR(rxq, Q_WM), 0x80);
25891816cb70SBarry Harding 	} else {
25901816cb70SBarry Harding 		CSR_WRITE_2(dev, Q_ADDR(rxq, Q_WM), MSK_BMU_RX_WM);
25911816cb70SBarry Harding 	}
25921816cb70SBarry Harding 	if (dev->d_hw_id == CHIP_ID_YUKON_EC_U &&
25931816cb70SBarry Harding 	    dev->d_hw_rev >= CHIP_REV_YU_EC_U_A1) {
25941816cb70SBarry Harding 		/* MAC Rx RAM Read is controlled by hardware. */
25951816cb70SBarry Harding 		CSR_WRITE_4(dev, Q_ADDR(rxq, Q_F), F_M_RX_RAM_DIS);
25961816cb70SBarry Harding 	}
25971816cb70SBarry Harding 
25981816cb70SBarry Harding 	yge_init_tx_ring(port);
25991816cb70SBarry Harding 
26001816cb70SBarry Harding 	/* Disable Rx checksum offload and RSS hash. */
26011816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR),
26021816cb70SBarry Harding 	    BMU_DIS_RX_CHKSUM | BMU_DIS_RX_RSS_HASH);
26031816cb70SBarry Harding 
26041816cb70SBarry Harding 	yge_init_rx_ring(port);
26051816cb70SBarry Harding 
26061816cb70SBarry Harding 	/* Configure interrupt handling. */
26071816cb70SBarry Harding 	if (port == dev->d_port[YGE_PORT_A]) {
26081816cb70SBarry Harding 		dev->d_intrmask |= Y2_IS_PORT_A;
26091816cb70SBarry Harding 		dev->d_intrhwemask |= Y2_HWE_L1_MASK;
26101816cb70SBarry Harding 	} else if (port == dev->d_port[YGE_PORT_B]) {
26111816cb70SBarry Harding 		dev->d_intrmask |= Y2_IS_PORT_B;
26121816cb70SBarry Harding 		dev->d_intrhwemask |= Y2_HWE_L2_MASK;
26131816cb70SBarry Harding 	}
26141816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, dev->d_intrhwemask);
26151816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
26161816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
26171816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
26181816cb70SBarry Harding 
26191816cb70SBarry Harding 	/* Enable RX/TX GMAC */
26201816cb70SBarry Harding 	gmac = GMAC_READ_2(dev, pnum, GM_GP_CTRL);
26211816cb70SBarry Harding 	gmac |= (GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
26221816cb70SBarry Harding 	GMAC_WRITE_2(port->p_dev, port->p_port, GM_GP_CTRL, gmac);
26231816cb70SBarry Harding 	/* Read again to ensure writing. */
26241816cb70SBarry Harding 	(void) GMAC_READ_2(dev, pnum, GM_GP_CTRL);
26251816cb70SBarry Harding 
26261816cb70SBarry Harding 	/* Reset TX timer */
26271816cb70SBarry Harding 	port->p_tx_wdog = 0;
26281816cb70SBarry Harding }
26291816cb70SBarry Harding 
26301816cb70SBarry Harding static void
26311816cb70SBarry Harding yge_set_rambuffer(yge_port_t *port)
26321816cb70SBarry Harding {
26331816cb70SBarry Harding 	yge_dev_t *dev;
26341816cb70SBarry Harding 	int ltpp, utpp;
26351816cb70SBarry Harding 	int pnum;
26361816cb70SBarry Harding 	uint32_t rxq;
26371816cb70SBarry Harding 	uint32_t txq;
26381816cb70SBarry Harding 
26391816cb70SBarry Harding 	dev = port->p_dev;
26401816cb70SBarry Harding 	pnum = port->p_port;
26411816cb70SBarry Harding 	rxq = port->p_rxq;
26421816cb70SBarry Harding 	txq = port->p_txq;
26431816cb70SBarry Harding 
26441816cb70SBarry Harding 	if ((port->p_flags & PORT_FLAG_RAMBUF) == 0)
26451816cb70SBarry Harding 		return;
26461816cb70SBarry Harding 
26471816cb70SBarry Harding 	/* Setup Rx Queue. */
26481816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_RST_CLR);
26491816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_START), dev->d_rxqstart[pnum] / 8);
26501816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_END), dev->d_rxqend[pnum] / 8);
26511816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_WP), dev->d_rxqstart[pnum] / 8);
26521816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RP), dev->d_rxqstart[pnum] / 8);
26531816cb70SBarry Harding 
26541816cb70SBarry Harding 	utpp =
26551816cb70SBarry Harding 	    (dev->d_rxqend[pnum] + 1 - dev->d_rxqstart[pnum] - RB_ULPP) / 8;
26561816cb70SBarry Harding 	ltpp =
26571816cb70SBarry Harding 	    (dev->d_rxqend[pnum] + 1 - dev->d_rxqstart[pnum] - RB_LLPP_B) / 8;
26581816cb70SBarry Harding 
26591816cb70SBarry Harding 	if (dev->d_rxqsize < MSK_MIN_RXQ_SIZE)
26601816cb70SBarry Harding 		ltpp += (RB_LLPP_B - RB_LLPP_S) / 8;
26611816cb70SBarry Harding 
26621816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RX_UTPP), utpp);
26631816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(rxq, RB_RX_LTPP), ltpp);
26641816cb70SBarry Harding 	/* Set Rx priority(RB_RX_UTHP/RB_RX_LTHP) thresholds? */
26651816cb70SBarry Harding 
26661816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_ENA_OP_MD);
26671816cb70SBarry Harding 	(void) CSR_READ_1(dev, RB_ADDR(rxq, RB_CTRL));
26681816cb70SBarry Harding 
26691816cb70SBarry Harding 	/* Setup Tx Queue. */
26701816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_CLR);
26711816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_START), dev->d_txqstart[pnum] / 8);
26721816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_END),  dev->d_txqend[pnum] / 8);
26731816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_WP), dev->d_txqstart[pnum] / 8);
26741816cb70SBarry Harding 	CSR_WRITE_4(dev, RB_ADDR(txq, RB_RP), dev->d_txqstart[pnum] / 8);
26751816cb70SBarry Harding 	/* Enable Store & Forward for Tx side. */
26761816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_ENA_STFWD);
26771816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_ENA_OP_MD);
26781816cb70SBarry Harding 	(void) CSR_READ_1(dev, RB_ADDR(txq, RB_CTRL));
26791816cb70SBarry Harding }
26801816cb70SBarry Harding 
26811816cb70SBarry Harding static void
26821816cb70SBarry Harding yge_set_prefetch(yge_dev_t *dev, int qaddr, yge_ring_t *ring)
26831816cb70SBarry Harding {
26841816cb70SBarry Harding 	/* Reset the prefetch unit. */
26851816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
26861816cb70SBarry Harding 	    PREF_UNIT_RST_SET);
26871816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
26881816cb70SBarry Harding 	    PREF_UNIT_RST_CLR);
26891816cb70SBarry Harding 	/* Set LE base address. */
26901816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_ADDR_LOW_REG),
26911816cb70SBarry Harding 	    YGE_ADDR_LO(ring->r_paddr));
26921816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_ADDR_HI_REG),
26931816cb70SBarry Harding 	    YGE_ADDR_HI(ring->r_paddr));
26941816cb70SBarry Harding 	/* Set the list last index. */
26951816cb70SBarry Harding 	CSR_WRITE_2(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_LAST_IDX_REG),
26961816cb70SBarry Harding 	    ring->r_num - 1);
26971816cb70SBarry Harding 	/* Turn on prefetch unit. */
26981816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG),
26991816cb70SBarry Harding 	    PREF_UNIT_OP_ON);
27001816cb70SBarry Harding 	/* Dummy read to ensure write. */
27011816cb70SBarry Harding 	(void) CSR_READ_4(dev, Y2_PREF_Q_ADDR(qaddr, PREF_UNIT_CTRL_REG));
27021816cb70SBarry Harding }
27031816cb70SBarry Harding 
27041816cb70SBarry Harding static void
27051816cb70SBarry Harding yge_stop_port(yge_port_t *port)
27061816cb70SBarry Harding {
27071816cb70SBarry Harding 	yge_dev_t *dev = port->p_dev;
27081816cb70SBarry Harding 	int pnum = port->p_port;
27091816cb70SBarry Harding 	uint32_t txq = port->p_txq;
27101816cb70SBarry Harding 	uint32_t rxq = port->p_rxq;
27111816cb70SBarry Harding 	uint32_t val;
27121816cb70SBarry Harding 	int i;
27131816cb70SBarry Harding 
27141816cb70SBarry Harding 	dev = port->p_dev;
27151816cb70SBarry Harding 
27161816cb70SBarry Harding 	/*
27171816cb70SBarry Harding 	 * shutdown timeout
27181816cb70SBarry Harding 	 */
27191816cb70SBarry Harding 	port->p_tx_wdog = 0;
27201816cb70SBarry Harding 
27211816cb70SBarry Harding 	/* Disable interrupts. */
27221816cb70SBarry Harding 	if (pnum == YGE_PORT_A) {
27231816cb70SBarry Harding 		dev->d_intrmask &= ~Y2_IS_PORT_A;
27241816cb70SBarry Harding 		dev->d_intrhwemask &= ~Y2_HWE_L1_MASK;
27251816cb70SBarry Harding 	} else {
27261816cb70SBarry Harding 		dev->d_intrmask &= ~Y2_IS_PORT_B;
27271816cb70SBarry Harding 		dev->d_intrhwemask &= ~Y2_HWE_L2_MASK;
27281816cb70SBarry Harding 	}
27291816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, dev->d_intrhwemask);
27301816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
27311816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, dev->d_intrmask);
27321816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
27331816cb70SBarry Harding 
27341816cb70SBarry Harding 	/* Disable Tx/Rx MAC. */
27351816cb70SBarry Harding 	val = GMAC_READ_2(dev, pnum, GM_GP_CTRL);
27361816cb70SBarry Harding 	val &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
27371816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_GP_CTRL, val);
27381816cb70SBarry Harding 	/* Read again to ensure writing. */
27391816cb70SBarry Harding 	(void) GMAC_READ_2(dev, pnum, GM_GP_CTRL);
27401816cb70SBarry Harding 
27411816cb70SBarry Harding 	/* Update stats and clear counters. */
27421816cb70SBarry Harding 	yge_stats_update(port);
27431816cb70SBarry Harding 
27441816cb70SBarry Harding 	/* Stop Tx BMU. */
27451816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_STOP);
27461816cb70SBarry Harding 	val = CSR_READ_4(dev, Q_ADDR(txq, Q_CSR));
27471816cb70SBarry Harding 	for (i = 0; i < YGE_TIMEOUT; i += 10) {
27481816cb70SBarry Harding 		if ((val & (BMU_STOP | BMU_IDLE)) == 0) {
27491816cb70SBarry Harding 			CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_STOP);
27501816cb70SBarry Harding 			val = CSR_READ_4(dev, Q_ADDR(txq, Q_CSR));
27511816cb70SBarry Harding 		} else
27521816cb70SBarry Harding 			break;
27531816cb70SBarry Harding 		drv_usecwait(10);
27541816cb70SBarry Harding 	}
27551816cb70SBarry Harding 	/* This is probably fairly catastrophic. */
27561816cb70SBarry Harding 	if ((val & (BMU_STOP | BMU_IDLE)) == 0)
27571816cb70SBarry Harding 		yge_error(NULL, port, "Tx BMU stop failed");
27581816cb70SBarry Harding 
27591816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_SET | RB_DIS_OP_MD);
27601816cb70SBarry Harding 
27611816cb70SBarry Harding 	/* Disable all GMAC interrupt. */
27621816cb70SBarry Harding 	CSR_WRITE_1(dev, MR_ADDR(pnum, GMAC_IRQ_MSK), 0);
27631816cb70SBarry Harding 
27641816cb70SBarry Harding 	/* Disable the RAM Interface Arbiter. */
27651816cb70SBarry Harding 	CSR_WRITE_1(dev, MR_ADDR(pnum, TXA_CTRL), TXA_DIS_ARB);
27661816cb70SBarry Harding 
27671816cb70SBarry Harding 	/* Reset the PCI FIFO of the async Tx queue */
27681816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(txq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
27691816cb70SBarry Harding 
27701816cb70SBarry Harding 	/* Reset the Tx prefetch units. */
27711816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(txq, PREF_UNIT_CTRL_REG),
27721816cb70SBarry Harding 	    PREF_UNIT_RST_SET);
27731816cb70SBarry Harding 
27741816cb70SBarry Harding 	/* Reset the RAM Buffer async Tx queue. */
27751816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(txq, RB_CTRL), RB_RST_SET);
27761816cb70SBarry Harding 
27771816cb70SBarry Harding 	/* Reset Tx MAC FIFO. */
27781816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, TX_GMF_CTRL_T), GMF_RST_SET);
27791816cb70SBarry Harding 	/* Set Pause Off. */
27801816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, GMAC_CTRL), GMC_PAUSE_OFF);
27811816cb70SBarry Harding 
27821816cb70SBarry Harding 	/*
27831816cb70SBarry Harding 	 * The Rx Stop command will not work for Yukon-2 if the BMU does not
27841816cb70SBarry Harding 	 * reach the end of packet and since we can't make sure that we have
27851816cb70SBarry Harding 	 * incoming data, we must reset the BMU while it is not during a DMA
27861816cb70SBarry Harding 	 * transfer. Since it is possible that the Rx path is still active,
27871816cb70SBarry Harding 	 * the Rx RAM buffer will be stopped first, so any possible incoming
27881816cb70SBarry Harding 	 * data will not trigger a DMA. After the RAM buffer is stopped, the
27891816cb70SBarry Harding 	 * BMU is polled until any DMA in progress is ended and only then it
27901816cb70SBarry Harding 	 * will be reset.
27911816cb70SBarry Harding 	 */
27921816cb70SBarry Harding 
27931816cb70SBarry Harding 	/* Disable the RAM Buffer receive queue. */
27941816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
27951816cb70SBarry Harding 	for (i = 0; i < YGE_TIMEOUT; i += 10) {
27961816cb70SBarry Harding 		if (CSR_READ_1(dev, RB_ADDR(rxq, Q_RSL)) ==
27971816cb70SBarry Harding 		    CSR_READ_1(dev, RB_ADDR(rxq, Q_RL)))
27981816cb70SBarry Harding 			break;
27991816cb70SBarry Harding 		drv_usecwait(10);
28001816cb70SBarry Harding 	}
28011816cb70SBarry Harding 	/* This is probably nearly a fatal error. */
28021816cb70SBarry Harding 	if (i == YGE_TIMEOUT)
28031816cb70SBarry Harding 		yge_error(NULL, port, "Rx BMU stop failed");
28041816cb70SBarry Harding 
28051816cb70SBarry Harding 	CSR_WRITE_4(dev, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
28061816cb70SBarry Harding 	/* Reset the Rx prefetch unit. */
28071816cb70SBarry Harding 	CSR_WRITE_4(dev, Y2_PREF_Q_ADDR(rxq, PREF_UNIT_CTRL_REG),
28081816cb70SBarry Harding 	    PREF_UNIT_RST_SET);
28091816cb70SBarry Harding 	/* Reset the RAM Buffer receive queue. */
28101816cb70SBarry Harding 	CSR_WRITE_1(dev, RB_ADDR(rxq, RB_CTRL), RB_RST_SET);
28111816cb70SBarry Harding 	/* Reset Rx MAC FIFO. */
28121816cb70SBarry Harding 	CSR_WRITE_4(dev, MR_ADDR(pnum, RX_GMF_CTRL_T), GMF_RST_SET);
28131816cb70SBarry Harding }
28141816cb70SBarry Harding 
28151816cb70SBarry Harding /*
28161816cb70SBarry Harding  * When GM_PAR_MIB_CLR bit of GM_PHY_ADDR is set, reading lower
28171816cb70SBarry Harding  * counter clears high 16 bits of the counter such that accessing
28181816cb70SBarry Harding  * lower 16 bits should be the last operation.
28191816cb70SBarry Harding  */
28201816cb70SBarry Harding #define	YGE_READ_MIB32(x, y)					\
28211816cb70SBarry Harding 	GMAC_READ_4(dev, x, y)
28221816cb70SBarry Harding 
28231816cb70SBarry Harding #define	YGE_READ_MIB64(x, y)					\
28241816cb70SBarry Harding 	((((uint64_t)YGE_READ_MIB32(x, (y) + 8)) << 32) +	\
28251816cb70SBarry Harding 	    (uint64_t)YGE_READ_MIB32(x, y))
28261816cb70SBarry Harding 
28271816cb70SBarry Harding static void
28281816cb70SBarry Harding yge_stats_clear(yge_port_t *port)
28291816cb70SBarry Harding {
28301816cb70SBarry Harding 	yge_dev_t *dev;
28311816cb70SBarry Harding 	uint16_t gmac;
28321816cb70SBarry Harding 	int32_t pnum;
28331816cb70SBarry Harding 
28341816cb70SBarry Harding 	pnum = port->p_port;
28351816cb70SBarry Harding 	dev = port->p_dev;
28361816cb70SBarry Harding 
28371816cb70SBarry Harding 	/* Set MIB Clear Counter Mode. */
28381816cb70SBarry Harding 	gmac = GMAC_READ_2(dev, pnum, GM_PHY_ADDR);
28391816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR);
28401816cb70SBarry Harding 	/* Read all MIB Counters with Clear Mode set. */
28411816cb70SBarry Harding 	for (int i = GM_RXF_UC_OK; i <= GM_TXE_FIFO_UR; i += 4)
28421816cb70SBarry Harding 		(void) YGE_READ_MIB32(pnum, i);
28431816cb70SBarry Harding 	/* Clear MIB Clear Counter Mode. */
28441816cb70SBarry Harding 	gmac &= ~GM_PAR_MIB_CLR;
28451816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac);
28461816cb70SBarry Harding }
28471816cb70SBarry Harding 
28481816cb70SBarry Harding static void
28491816cb70SBarry Harding yge_stats_update(yge_port_t *port)
28501816cb70SBarry Harding {
28511816cb70SBarry Harding 	yge_dev_t *dev;
28521816cb70SBarry Harding 	struct yge_hw_stats *stats;
28531816cb70SBarry Harding 	uint16_t gmac;
28541816cb70SBarry Harding 	int32_t	pnum;
28551816cb70SBarry Harding 
28561816cb70SBarry Harding 	dev = port->p_dev;
28571816cb70SBarry Harding 	pnum = port->p_port;
28581816cb70SBarry Harding 
28591816cb70SBarry Harding 	if (dev->d_suspended || !port->p_running) {
28601816cb70SBarry Harding 		return;
28611816cb70SBarry Harding 	}
28621816cb70SBarry Harding 	stats = &port->p_stats;
28631816cb70SBarry Harding 	/* Set MIB Clear Counter Mode. */
28641816cb70SBarry Harding 	gmac = GMAC_READ_2(dev, pnum, GM_PHY_ADDR);
28651816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac | GM_PAR_MIB_CLR);
28661816cb70SBarry Harding 
28671816cb70SBarry Harding 	/* Rx stats. */
28681816cb70SBarry Harding 	stats->rx_ucast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_UC_OK);
28691816cb70SBarry Harding 	stats->rx_bcast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_BC_OK);
28701816cb70SBarry Harding 	stats->rx_pause_frames +=	YGE_READ_MIB32(pnum, GM_RXF_MPAUSE);
28711816cb70SBarry Harding 	stats->rx_mcast_frames +=	YGE_READ_MIB32(pnum, GM_RXF_MC_OK);
28721816cb70SBarry Harding 	stats->rx_crc_errs +=		YGE_READ_MIB32(pnum, GM_RXF_FCS_ERR);
28731816cb70SBarry Harding 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE1);
28741816cb70SBarry Harding 	stats->rx_good_octets +=	YGE_READ_MIB64(pnum, GM_RXO_OK_LO);
28751816cb70SBarry Harding 	stats->rx_bad_octets +=		YGE_READ_MIB64(pnum, GM_RXO_ERR_LO);
28761816cb70SBarry Harding 	stats->rx_runts +=		YGE_READ_MIB32(pnum, GM_RXF_SHT);
28771816cb70SBarry Harding 	stats->rx_runt_errs +=		YGE_READ_MIB32(pnum, GM_RXE_FRAG);
28781816cb70SBarry Harding 	stats->rx_pkts_64 +=		YGE_READ_MIB32(pnum, GM_RXF_64B);
28791816cb70SBarry Harding 	stats->rx_pkts_65_127 +=	YGE_READ_MIB32(pnum, GM_RXF_127B);
28801816cb70SBarry Harding 	stats->rx_pkts_128_255 +=	YGE_READ_MIB32(pnum, GM_RXF_255B);
28811816cb70SBarry Harding 	stats->rx_pkts_256_511 +=	YGE_READ_MIB32(pnum, GM_RXF_511B);
28821816cb70SBarry Harding 	stats->rx_pkts_512_1023 +=	YGE_READ_MIB32(pnum, GM_RXF_1023B);
28831816cb70SBarry Harding 	stats->rx_pkts_1024_1518 +=	YGE_READ_MIB32(pnum, GM_RXF_1518B);
28841816cb70SBarry Harding 	stats->rx_pkts_1519_max +=	YGE_READ_MIB32(pnum, GM_RXF_MAX_SZ);
28851816cb70SBarry Harding 	stats->rx_pkts_too_long +=	YGE_READ_MIB32(pnum, GM_RXF_LNG_ERR);
28861816cb70SBarry Harding 	stats->rx_pkts_jabbers +=	YGE_READ_MIB32(pnum, GM_RXF_JAB_PKT);
28871816cb70SBarry Harding 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE2);
28881816cb70SBarry Harding 	stats->rx_fifo_oflows +=	YGE_READ_MIB32(pnum, GM_RXE_FIFO_OV);
28891816cb70SBarry Harding 	(void) YGE_READ_MIB32(pnum, GM_RXF_SPARE3);
28901816cb70SBarry Harding 
28911816cb70SBarry Harding 	/* Tx stats. */
28921816cb70SBarry Harding 	stats->tx_ucast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_UC_OK);
28931816cb70SBarry Harding 	stats->tx_bcast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_BC_OK);
28941816cb70SBarry Harding 	stats->tx_pause_frames +=	YGE_READ_MIB32(pnum, GM_TXF_MPAUSE);
28951816cb70SBarry Harding 	stats->tx_mcast_frames +=	YGE_READ_MIB32(pnum, GM_TXF_MC_OK);
28961816cb70SBarry Harding 	stats->tx_octets +=		YGE_READ_MIB64(pnum, GM_TXO_OK_LO);
28971816cb70SBarry Harding 	stats->tx_pkts_64 +=		YGE_READ_MIB32(pnum, GM_TXF_64B);
28981816cb70SBarry Harding 	stats->tx_pkts_65_127 +=	YGE_READ_MIB32(pnum, GM_TXF_127B);
28991816cb70SBarry Harding 	stats->tx_pkts_128_255 +=	YGE_READ_MIB32(pnum, GM_TXF_255B);
29001816cb70SBarry Harding 	stats->tx_pkts_256_511 +=	YGE_READ_MIB32(pnum, GM_TXF_511B);
29011816cb70SBarry Harding 	stats->tx_pkts_512_1023 +=	YGE_READ_MIB32(pnum, GM_TXF_1023B);
29021816cb70SBarry Harding 	stats->tx_pkts_1024_1518 +=	YGE_READ_MIB32(pnum, GM_TXF_1518B);
29031816cb70SBarry Harding 	stats->tx_pkts_1519_max +=	YGE_READ_MIB32(pnum, GM_TXF_MAX_SZ);
29041816cb70SBarry Harding 	(void) YGE_READ_MIB32(pnum, GM_TXF_SPARE1);
29051816cb70SBarry Harding 	stats->tx_colls +=		YGE_READ_MIB32(pnum, GM_TXF_COL);
29061816cb70SBarry Harding 	stats->tx_late_colls +=		YGE_READ_MIB32(pnum, GM_TXF_LAT_COL);
29071816cb70SBarry Harding 	stats->tx_excess_colls +=	YGE_READ_MIB32(pnum, GM_TXF_ABO_COL);
29081816cb70SBarry Harding 	stats->tx_multi_colls +=	YGE_READ_MIB32(pnum, GM_TXF_MUL_COL);
29091816cb70SBarry Harding 	stats->tx_single_colls +=	YGE_READ_MIB32(pnum, GM_TXF_SNG_COL);
29101816cb70SBarry Harding 	stats->tx_underflows +=		YGE_READ_MIB32(pnum, GM_TXE_FIFO_UR);
29111816cb70SBarry Harding 	/* Clear MIB Clear Counter Mode. */
29121816cb70SBarry Harding 	gmac &= ~GM_PAR_MIB_CLR;
29131816cb70SBarry Harding 	GMAC_WRITE_2(dev, pnum, GM_PHY_ADDR, gmac);
29141816cb70SBarry Harding }
29151816cb70SBarry Harding 
29161816cb70SBarry Harding #undef YGE_READ_MIB32
29171816cb70SBarry Harding #undef YGE_READ_MIB64
29181816cb70SBarry Harding 
29191816cb70SBarry Harding uint32_t
29201816cb70SBarry Harding yge_hashbit(const uint8_t *addr)
29211816cb70SBarry Harding {
29221816cb70SBarry Harding 	int		idx;
29231816cb70SBarry Harding 	int		bit;
29241816cb70SBarry Harding 	uint_t		data;
29251816cb70SBarry Harding 	uint32_t	crc;
29261816cb70SBarry Harding #define	POLY_BE	0x04c11db7
29271816cb70SBarry Harding 
29281816cb70SBarry Harding 	crc = 0xffffffff;
29291816cb70SBarry Harding 	for (idx = 0; idx < 6; idx++) {
29301816cb70SBarry Harding 		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
29311816cb70SBarry Harding 			crc = (crc << 1)
29321816cb70SBarry Harding 			    ^ ((((crc >> 31) ^ data) & 1) ? POLY_BE : 0);
29331816cb70SBarry Harding 		}
29341816cb70SBarry Harding 	}
29351816cb70SBarry Harding #undef	POLY_BE
29361816cb70SBarry Harding 
29371816cb70SBarry Harding 	return (crc % 64);
29381816cb70SBarry Harding }
29391816cb70SBarry Harding 
29401816cb70SBarry Harding int
29411816cb70SBarry Harding yge_m_stat(void *arg, uint_t stat, uint64_t *val)
29421816cb70SBarry Harding {
29431816cb70SBarry Harding 	yge_port_t	*port = arg;
29441816cb70SBarry Harding 	struct yge_hw_stats *stats = &port->p_stats;
29451816cb70SBarry Harding 
29461816cb70SBarry Harding 	if (stat == MAC_STAT_IFSPEED) {
29471816cb70SBarry Harding 		/*
29481816cb70SBarry Harding 		 * This is the first stat we are asked about.  We update only
29491816cb70SBarry Harding 		 * for this stat, to avoid paying the hefty cost of the update
29501816cb70SBarry Harding 		 * once for each stat.
29511816cb70SBarry Harding 		 */
29521816cb70SBarry Harding 		DEV_LOCK(port->p_dev);
29531816cb70SBarry Harding 		yge_stats_update(port);
29541816cb70SBarry Harding 		DEV_UNLOCK(port->p_dev);
29551816cb70SBarry Harding 	}
29561816cb70SBarry Harding 
29571816cb70SBarry Harding 	if (mii_m_getstat(port->p_mii, stat, val) == 0) {
29581816cb70SBarry Harding 		return (0);
29591816cb70SBarry Harding 	}
29601816cb70SBarry Harding 
29611816cb70SBarry Harding 	switch (stat) {
29621816cb70SBarry Harding 	case MAC_STAT_MULTIRCV:
29631816cb70SBarry Harding 		*val = stats->rx_mcast_frames;
29641816cb70SBarry Harding 		break;
29651816cb70SBarry Harding 
29661816cb70SBarry Harding 	case MAC_STAT_BRDCSTRCV:
29671816cb70SBarry Harding 		*val = stats->rx_bcast_frames;
29681816cb70SBarry Harding 		break;
29691816cb70SBarry Harding 
29701816cb70SBarry Harding 	case MAC_STAT_MULTIXMT:
29711816cb70SBarry Harding 		*val = stats->tx_mcast_frames;
29721816cb70SBarry Harding 		break;
29731816cb70SBarry Harding 
29741816cb70SBarry Harding 	case MAC_STAT_BRDCSTXMT:
29751816cb70SBarry Harding 		*val = stats->tx_bcast_frames;
29761816cb70SBarry Harding 		break;
29771816cb70SBarry Harding 
29781816cb70SBarry Harding 	case MAC_STAT_IPACKETS:
29791816cb70SBarry Harding 		*val = stats->rx_ucast_frames;
29801816cb70SBarry Harding 		break;
29811816cb70SBarry Harding 
29821816cb70SBarry Harding 	case MAC_STAT_RBYTES:
29831816cb70SBarry Harding 		*val = stats->rx_good_octets;
29841816cb70SBarry Harding 		break;
29851816cb70SBarry Harding 
29861816cb70SBarry Harding 	case MAC_STAT_OPACKETS:
29871816cb70SBarry Harding 		*val = stats->tx_ucast_frames;
29881816cb70SBarry Harding 		break;
29891816cb70SBarry Harding 
29901816cb70SBarry Harding 	case MAC_STAT_OBYTES:
29911816cb70SBarry Harding 		*val = stats->tx_octets;
29921816cb70SBarry Harding 		break;
29931816cb70SBarry Harding 
29941816cb70SBarry Harding 	case MAC_STAT_NORCVBUF:
29951816cb70SBarry Harding 		*val = stats->rx_nobuf;
29961816cb70SBarry Harding 		break;
29971816cb70SBarry Harding 
29981816cb70SBarry Harding 	case MAC_STAT_COLLISIONS:
29991816cb70SBarry Harding 		*val = stats->tx_colls;
30001816cb70SBarry Harding 		break;
30011816cb70SBarry Harding 
30021816cb70SBarry Harding 	case ETHER_STAT_ALIGN_ERRORS:
30031816cb70SBarry Harding 		*val = stats->rx_runt_errs;
30041816cb70SBarry Harding 		break;
30051816cb70SBarry Harding 
30061816cb70SBarry Harding 	case ETHER_STAT_FCS_ERRORS:
30071816cb70SBarry Harding 		*val = stats->rx_crc_errs;
30081816cb70SBarry Harding 		break;
30091816cb70SBarry Harding 
30101816cb70SBarry Harding 	case ETHER_STAT_FIRST_COLLISIONS:
30111816cb70SBarry Harding 		*val  = stats->tx_single_colls;
30121816cb70SBarry Harding 		break;
30131816cb70SBarry Harding 
30141816cb70SBarry Harding 	case ETHER_STAT_MULTI_COLLISIONS:
30151816cb70SBarry Harding 		*val = stats->tx_multi_colls;
30161816cb70SBarry Harding 		break;
30171816cb70SBarry Harding 
30181816cb70SBarry Harding 	case ETHER_STAT_TX_LATE_COLLISIONS:
30191816cb70SBarry Harding 		*val = stats->tx_late_colls;
30201816cb70SBarry Harding 		break;
30211816cb70SBarry Harding 
30221816cb70SBarry Harding 	case ETHER_STAT_EX_COLLISIONS:
30231816cb70SBarry Harding 		*val = stats->tx_excess_colls;
30241816cb70SBarry Harding 		break;
30251816cb70SBarry Harding 
30261816cb70SBarry Harding 	case ETHER_STAT_TOOLONG_ERRORS:
30271816cb70SBarry Harding 		*val = stats->rx_pkts_too_long;
30281816cb70SBarry Harding 		break;
30291816cb70SBarry Harding 
30301816cb70SBarry Harding 	case MAC_STAT_OVERFLOWS:
30311816cb70SBarry Harding 		*val = stats->rx_fifo_oflows;
30321816cb70SBarry Harding 		break;
30331816cb70SBarry Harding 
30341816cb70SBarry Harding 	case MAC_STAT_UNDERFLOWS:
30351816cb70SBarry Harding 		*val = stats->tx_underflows;
30361816cb70SBarry Harding 		break;
30371816cb70SBarry Harding 
30381816cb70SBarry Harding 	case ETHER_STAT_TOOSHORT_ERRORS:
30391816cb70SBarry Harding 		*val = stats->rx_runts;
30401816cb70SBarry Harding 		break;
30411816cb70SBarry Harding 
30421816cb70SBarry Harding 	case ETHER_STAT_JABBER_ERRORS:
30431816cb70SBarry Harding 		*val = stats->rx_pkts_jabbers;
30441816cb70SBarry Harding 		break;
30451816cb70SBarry Harding 
30461816cb70SBarry Harding 	default:
30471816cb70SBarry Harding 		return (ENOTSUP);
30481816cb70SBarry Harding 	}
30491816cb70SBarry Harding 	return (0);
30501816cb70SBarry Harding }
30511816cb70SBarry Harding 
30521816cb70SBarry Harding int
30531816cb70SBarry Harding yge_m_start(void *arg)
30541816cb70SBarry Harding {
30551816cb70SBarry Harding 	yge_port_t	*port = arg;
30561816cb70SBarry Harding 
30571816cb70SBarry Harding 	DEV_LOCK(port->p_dev);
30581816cb70SBarry Harding 
30591816cb70SBarry Harding 	/*
30601816cb70SBarry Harding 	 * We defer resource allocation to this point, because we
30611816cb70SBarry Harding 	 * don't want to waste DMA resources that might better be used
30621816cb70SBarry Harding 	 * elsewhere, if the port is not actually being used.
30631816cb70SBarry Harding 	 *
30641816cb70SBarry Harding 	 * Furthermore, this gives us a more graceful handling of dynamic
30651816cb70SBarry Harding 	 * MTU modification.
30661816cb70SBarry Harding 	 */
30671816cb70SBarry Harding 	if (yge_txrx_dma_alloc(port) != DDI_SUCCESS) {
30681816cb70SBarry Harding 		/* Make sure we free up partially allocated resources. */
30691816cb70SBarry Harding 		yge_txrx_dma_free(port);
30701816cb70SBarry Harding 		DEV_UNLOCK(port->p_dev);
30711816cb70SBarry Harding 		return (ENOMEM);
30721816cb70SBarry Harding 	}
30731816cb70SBarry Harding 
30741816cb70SBarry Harding 	if (!port->p_dev->d_suspended)
30751816cb70SBarry Harding 		yge_start_port(port);
30761816cb70SBarry Harding 	port->p_running = B_TRUE;
30771816cb70SBarry Harding 	DEV_UNLOCK(port->p_dev);
30781816cb70SBarry Harding 
30791816cb70SBarry Harding 	mii_start(port->p_mii);
30801816cb70SBarry Harding 
30811816cb70SBarry Harding 	return (0);
30821816cb70SBarry Harding }
30831816cb70SBarry Harding 
30841816cb70SBarry Harding void
30851816cb70SBarry Harding yge_m_stop(void *arg)
30861816cb70SBarry Harding {
30871816cb70SBarry Harding 	yge_port_t	*port = arg;
30881816cb70SBarry Harding 	yge_dev_t	*dev = port->p_dev;
30891816cb70SBarry Harding 
30901816cb70SBarry Harding 	DEV_LOCK(dev);
30911816cb70SBarry Harding 	if (!dev->d_suspended)
30921816cb70SBarry Harding 		yge_stop_port(port);
30931816cb70SBarry Harding 
30941816cb70SBarry Harding 	port->p_running = B_FALSE;
30951816cb70SBarry Harding 
30961816cb70SBarry Harding 	/* Release resources we don't need */
30971816cb70SBarry Harding 	yge_txrx_dma_free(port);
30981816cb70SBarry Harding 	DEV_UNLOCK(dev);
30991816cb70SBarry Harding }
31001816cb70SBarry Harding 
31011816cb70SBarry Harding int
31021816cb70SBarry Harding yge_m_promisc(void *arg, boolean_t on)
31031816cb70SBarry Harding {
31041816cb70SBarry Harding 	yge_port_t	*port = arg;
31051816cb70SBarry Harding 
31061816cb70SBarry Harding 	DEV_LOCK(port->p_dev);
31071816cb70SBarry Harding 
31081816cb70SBarry Harding 	/* Save current promiscuous mode. */
31091816cb70SBarry Harding 	port->p_promisc = on;
31101816cb70SBarry Harding 	yge_setrxfilt(port);
31111816cb70SBarry Harding 
31121816cb70SBarry Harding 	DEV_UNLOCK(port->p_dev);
31131816cb70SBarry Harding 
31141816cb70SBarry Harding 	return (0);
31151816cb70SBarry Harding }
31161816cb70SBarry Harding 
31171816cb70SBarry Harding int
31181816cb70SBarry Harding yge_m_multicst(void *arg, boolean_t add, const uint8_t *addr)
31191816cb70SBarry Harding {
31201816cb70SBarry Harding 	yge_port_t	*port = arg;
31211816cb70SBarry Harding 	int		bit;
31221816cb70SBarry Harding 	boolean_t	update;
31231816cb70SBarry Harding 
31241816cb70SBarry Harding 	bit = yge_hashbit(addr);
31251816cb70SBarry Harding 	ASSERT(bit < 64);
31261816cb70SBarry Harding 
31271816cb70SBarry Harding 	DEV_LOCK(port->p_dev);
31281816cb70SBarry Harding 	if (add) {
31291816cb70SBarry Harding 		if (port->p_mccount[bit] == 0) {
31301816cb70SBarry Harding 			/* Set the corresponding bit in the hash table. */
31311816cb70SBarry Harding 			port->p_mchash[bit / 32] |= (1 << (bit % 32));
31321816cb70SBarry Harding 			update = B_TRUE;
31331816cb70SBarry Harding 		}
31341816cb70SBarry Harding 		port->p_mccount[bit]++;
31351816cb70SBarry Harding 	} else {
31361816cb70SBarry Harding 		ASSERT(port->p_mccount[bit] > 0);
31371816cb70SBarry Harding 		port->p_mccount[bit]--;
31381816cb70SBarry Harding 		if (port->p_mccount[bit] == 0) {
31391816cb70SBarry Harding 			port->p_mchash[bit / 32] &= ~(1 << (bit % 32));
31401816cb70SBarry Harding 			update = B_TRUE;
31411816cb70SBarry Harding 		}
31421816cb70SBarry Harding 	}
31431816cb70SBarry Harding 
31441816cb70SBarry Harding 	if (update) {
31451816cb70SBarry Harding 		yge_setrxfilt(port);
31461816cb70SBarry Harding 	}
31471816cb70SBarry Harding 	DEV_UNLOCK(port->p_dev);
31481816cb70SBarry Harding 	return (0);
31491816cb70SBarry Harding }
31501816cb70SBarry Harding 
31511816cb70SBarry Harding int
31521816cb70SBarry Harding yge_m_unicst(void *arg, const uint8_t *macaddr)
31531816cb70SBarry Harding {
31541816cb70SBarry Harding 	yge_port_t	*port = arg;
31551816cb70SBarry Harding 
31561816cb70SBarry Harding 	DEV_LOCK(port->p_dev);
31571816cb70SBarry Harding 
31581816cb70SBarry Harding 	bcopy(macaddr, port->p_curraddr, ETHERADDRL);
31591816cb70SBarry Harding 	yge_setrxfilt(port);
31601816cb70SBarry Harding 
31611816cb70SBarry Harding 	DEV_UNLOCK(port->p_dev);
31621816cb70SBarry Harding 
31631816cb70SBarry Harding 	return (0);
31641816cb70SBarry Harding }
31651816cb70SBarry Harding 
31661816cb70SBarry Harding mblk_t *
31671816cb70SBarry Harding yge_m_tx(void *arg, mblk_t *mp)
31681816cb70SBarry Harding {
31691816cb70SBarry Harding 	yge_port_t	*port = arg;
31701816cb70SBarry Harding 	mblk_t		*nmp;
31711816cb70SBarry Harding 	int		enq = 0;
31721816cb70SBarry Harding 	uint32_t	ridx;
31731816cb70SBarry Harding 	int		idx;
31741816cb70SBarry Harding 	boolean_t	resched = B_FALSE;
31751816cb70SBarry Harding 
31761816cb70SBarry Harding 	TX_LOCK(port->p_dev);
31771816cb70SBarry Harding 
31781816cb70SBarry Harding 	if (port->p_dev->d_suspended) {
31791816cb70SBarry Harding 
31801816cb70SBarry Harding 		TX_UNLOCK(port->p_dev);
31811816cb70SBarry Harding 
31821816cb70SBarry Harding 		while ((nmp = mp) != NULL) {
31831816cb70SBarry Harding 			/* carrier_errors++; */
31841816cb70SBarry Harding 			mp = mp->b_next;
31851816cb70SBarry Harding 			freemsg(nmp);
31861816cb70SBarry Harding 		}
31871816cb70SBarry Harding 		return (NULL);
31881816cb70SBarry Harding 	}
31891816cb70SBarry Harding 
31901816cb70SBarry Harding 	/* attempt a reclaim */
31911816cb70SBarry Harding 	ridx = port->p_port == YGE_PORT_A ?
31921816cb70SBarry Harding 	    STAT_TXA1_RIDX : STAT_TXA2_RIDX;
31931816cb70SBarry Harding 	idx = CSR_READ_2(port->p_dev, ridx);
31941816cb70SBarry Harding 	if (port->p_tx_cons != idx)
31951816cb70SBarry Harding 		resched = yge_txeof_locked(port, idx);
31961816cb70SBarry Harding 
31971816cb70SBarry Harding 	while (mp != NULL) {
31981816cb70SBarry Harding 		nmp = mp->b_next;
31991816cb70SBarry Harding 		mp->b_next = NULL;
32001816cb70SBarry Harding 
32011816cb70SBarry Harding 		if (!yge_send(port, mp)) {
32021816cb70SBarry Harding 			mp->b_next = nmp;
32031816cb70SBarry Harding 			break;
32041816cb70SBarry Harding 		}
32051816cb70SBarry Harding 		enq++;
32061816cb70SBarry Harding 		mp = nmp;
32071816cb70SBarry Harding 
32081816cb70SBarry Harding 	}
32091816cb70SBarry Harding 	if (enq > 0) {
32101816cb70SBarry Harding 		/* Transmit */
32111816cb70SBarry Harding 		CSR_WRITE_2(port->p_dev,
32121816cb70SBarry Harding 		    Y2_PREF_Q_ADDR(port->p_txq, PREF_UNIT_PUT_IDX_REG),
32131816cb70SBarry Harding 		    port->p_tx_prod);
32141816cb70SBarry Harding 	}
32151816cb70SBarry Harding 
32161816cb70SBarry Harding 	TX_UNLOCK(port->p_dev);
32171816cb70SBarry Harding 
32181816cb70SBarry Harding 	if (resched)
32191816cb70SBarry Harding 		mac_tx_update(port->p_mh);
32201816cb70SBarry Harding 
32211816cb70SBarry Harding 	return (mp);
32221816cb70SBarry Harding }
32231816cb70SBarry Harding 
32241816cb70SBarry Harding void
32251816cb70SBarry Harding yge_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
32261816cb70SBarry Harding {
32271816cb70SBarry Harding #ifdef	YGE_MII_LOOPBACK
32281816cb70SBarry Harding 	/* LINTED E_FUNC_SET_NOT_USED */
32291816cb70SBarry Harding 	yge_port_t	*port = arg;
32301816cb70SBarry Harding 
32311816cb70SBarry Harding 	/*
32321816cb70SBarry Harding 	 * Right now, the MII common layer does not properly handle
32331816cb70SBarry Harding 	 * loopback on these PHYs.  Fixing this should be done at some
32341816cb70SBarry Harding 	 * point in the future.
32351816cb70SBarry Harding 	 */
32361816cb70SBarry Harding 	if (mii_m_loop_ioctl(port->p_mii, wq, mp))
32371816cb70SBarry Harding 		return;
32381816cb70SBarry Harding #else
32391816cb70SBarry Harding 	_NOTE(ARGUNUSED(arg));
32401816cb70SBarry Harding #endif
32411816cb70SBarry Harding 
32421816cb70SBarry Harding 	miocnak(wq, mp, 0, EINVAL);
32431816cb70SBarry Harding }
32441816cb70SBarry Harding 
32451816cb70SBarry Harding int
32461816cb70SBarry Harding yge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
32471816cb70SBarry Harding     uint_t pr_valsize, const void *pr_val)
32481816cb70SBarry Harding {
32491816cb70SBarry Harding 	yge_port_t	*port = arg;
32501816cb70SBarry Harding 	uint32_t	new_mtu;
32511816cb70SBarry Harding 	int err = 0;
32521816cb70SBarry Harding 
32531816cb70SBarry Harding 	err = mii_m_setprop(port->p_mii, pr_name, pr_num, pr_valsize, pr_val);
32541816cb70SBarry Harding 	if (err != ENOTSUP) {
32551816cb70SBarry Harding 		return (err);
32561816cb70SBarry Harding 	}
32571816cb70SBarry Harding 
32581816cb70SBarry Harding 	DEV_LOCK(port->p_dev);
32591816cb70SBarry Harding 
32601816cb70SBarry Harding 	switch (pr_num) {
32611816cb70SBarry Harding 	case MAC_PROP_MTU:
32621816cb70SBarry Harding 		if (pr_valsize < sizeof (new_mtu)) {
32631816cb70SBarry Harding 			err = EINVAL;
32641816cb70SBarry Harding 			break;
32651816cb70SBarry Harding 		}
32661816cb70SBarry Harding 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
32671816cb70SBarry Harding 		if (new_mtu == port->p_mtu) {
32681816cb70SBarry Harding 			/* no change */
32691816cb70SBarry Harding 			err = 0;
32701816cb70SBarry Harding 			break;
32711816cb70SBarry Harding 		}
32721816cb70SBarry Harding 		if (new_mtu < ETHERMTU) {
32731816cb70SBarry Harding 			yge_error(NULL, port,
32741816cb70SBarry Harding 			    "Maximum MTU size too small: %d", new_mtu);
32751816cb70SBarry Harding 			err = EINVAL;
32761816cb70SBarry Harding 			break;
32771816cb70SBarry Harding 		}
32781816cb70SBarry Harding 		if (new_mtu > (port->p_flags & PORT_FLAG_NOJUMBO ?
32791816cb70SBarry Harding 		    ETHERMTU : YGE_JUMBO_MTU)) {
32801816cb70SBarry Harding 			yge_error(NULL, port,
32811816cb70SBarry Harding 			    "Maximum MTU size too big: %d", new_mtu);
32821816cb70SBarry Harding 			err = EINVAL;
32831816cb70SBarry Harding 			break;
32841816cb70SBarry Harding 		}
32851816cb70SBarry Harding 		if (port->p_running) {
32861816cb70SBarry Harding 			yge_error(NULL, port,
32871816cb70SBarry Harding 			    "Unable to change maximum MTU while running");
32881816cb70SBarry Harding 			err = EBUSY;
32891816cb70SBarry Harding 			break;
32901816cb70SBarry Harding 		}
32911816cb70SBarry Harding 
32921816cb70SBarry Harding 
32931816cb70SBarry Harding 		/*
32941816cb70SBarry Harding 		 * NB: It would probably be better not to hold the
32951816cb70SBarry Harding 		 * DEVLOCK, but releasing it creates a potential race
32961816cb70SBarry Harding 		 * if m_start is called concurrently.
32971816cb70SBarry Harding 		 *
32981816cb70SBarry Harding 		 * It turns out that the MAC layer guarantees safety
32991816cb70SBarry Harding 		 * for us here by using a cut out for this kind of
33001816cb70SBarry Harding 		 * notification call back anyway.
33011816cb70SBarry Harding 		 *
33021816cb70SBarry Harding 		 * See R8. and R14. in mac.c locking comments, which read
33031816cb70SBarry Harding 		 * as follows:
33041816cb70SBarry Harding 		 *
33051816cb70SBarry Harding 		 * R8. Since it is not guaranteed (see R14) that
33061816cb70SBarry Harding 		 * drivers won't hold locks across mac driver
33071816cb70SBarry Harding 		 * interfaces, the MAC layer must provide a cut out
33081816cb70SBarry Harding 		 * for control interfaces like upcall notifications
33091816cb70SBarry Harding 		 * and start them in a separate thread.
33101816cb70SBarry Harding 		 *
33111816cb70SBarry Harding 		 * R14. It would be preferable if MAC drivers don't
33121816cb70SBarry Harding 		 * hold any locks across any mac call. However at a
33131816cb70SBarry Harding 		 * minimum they must not hold any locks across data
33141816cb70SBarry Harding 		 * upcalls. They must also make sure that all
33151816cb70SBarry Harding 		 * references to mac data structures are cleaned up
33161816cb70SBarry Harding 		 * and that it is single threaded at mac_unregister
33171816cb70SBarry Harding 		 * time.
33181816cb70SBarry Harding 		 */
33191816cb70SBarry Harding 		err = mac_maxsdu_update(port->p_mh, new_mtu);
33201816cb70SBarry Harding 		if (err != 0) {
33211816cb70SBarry Harding 			/* This should never occur! */
33221816cb70SBarry Harding 			yge_error(NULL, port,
33231816cb70SBarry Harding 			    "Failed notifying GLDv3 of new maximum MTU");
33241816cb70SBarry Harding 		} else {
33251816cb70SBarry Harding 			port->p_mtu = new_mtu;
33261816cb70SBarry Harding 		}
33271816cb70SBarry Harding 		break;
33281816cb70SBarry Harding 
33291816cb70SBarry Harding 	default:
33301816cb70SBarry Harding 		err = ENOTSUP;
33311816cb70SBarry Harding 		break;
33321816cb70SBarry Harding 	}
33331816cb70SBarry Harding 
33341816cb70SBarry Harding err:
33351816cb70SBarry Harding 	DEV_UNLOCK(port->p_dev);
33361816cb70SBarry Harding 
33371816cb70SBarry Harding 	return (err);
33381816cb70SBarry Harding }
33391816cb70SBarry Harding 
33401816cb70SBarry Harding int
33411816cb70SBarry Harding yge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
33420dc2366fSVenugopal Iyer     uint_t pr_valsize, void *pr_val)
33431816cb70SBarry Harding {
33441816cb70SBarry Harding 	yge_port_t	*port = arg;
33451816cb70SBarry Harding 
33460dc2366fSVenugopal Iyer 	return (mii_m_getprop(port->p_mii, pr_name, pr_num, pr_valsize,
33470dc2366fSVenugopal Iyer 	    pr_val));
33481816cb70SBarry Harding }
33491816cb70SBarry Harding 
33500dc2366fSVenugopal Iyer static void
33510dc2366fSVenugopal Iyer yge_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
33520dc2366fSVenugopal Iyer     mac_prop_info_handle_t prh)
33530dc2366fSVenugopal Iyer {
33540dc2366fSVenugopal Iyer 	yge_port_t	*port = arg;
33551816cb70SBarry Harding 
33561816cb70SBarry Harding 	switch (pr_num) {
33571816cb70SBarry Harding 	case MAC_PROP_MTU:
33580dc2366fSVenugopal Iyer 		mac_prop_info_set_range_uint32(prh, ETHERMTU,
33591816cb70SBarry Harding 		    port->p_flags & PORT_FLAG_NOJUMBO ?
33600dc2366fSVenugopal Iyer 		    ETHERMTU : YGE_JUMBO_MTU);
33611816cb70SBarry Harding 		break;
33621816cb70SBarry Harding 	default:
33630dc2366fSVenugopal Iyer 		mii_m_propinfo(port->p_mii, pr_name, pr_num, prh);
33641816cb70SBarry Harding 		break;
33651816cb70SBarry Harding 	}
33661816cb70SBarry Harding }
33671816cb70SBarry Harding 
33681816cb70SBarry Harding void
33691816cb70SBarry Harding yge_dispatch(yge_dev_t *dev, int flag)
33701816cb70SBarry Harding {
33711816cb70SBarry Harding 	TASK_LOCK(dev);
33721816cb70SBarry Harding 	dev->d_task_flags |= flag;
33731816cb70SBarry Harding 	TASK_SIGNAL(dev);
33741816cb70SBarry Harding 	TASK_UNLOCK(dev);
33751816cb70SBarry Harding }
33761816cb70SBarry Harding 
33771816cb70SBarry Harding void
33781816cb70SBarry Harding yge_task(void *arg)
33791816cb70SBarry Harding {
33801816cb70SBarry Harding 	yge_dev_t	*dev = arg;
33811816cb70SBarry Harding 	int		flags;
33821816cb70SBarry Harding 
33831816cb70SBarry Harding 	for (;;) {
33841816cb70SBarry Harding 
33851816cb70SBarry Harding 		TASK_LOCK(dev);
33861816cb70SBarry Harding 		while ((flags = dev->d_task_flags) == 0)
33871816cb70SBarry Harding 			TASK_WAIT(dev);
33881816cb70SBarry Harding 
33891816cb70SBarry Harding 		dev->d_task_flags = 0;
33901816cb70SBarry Harding 		TASK_UNLOCK(dev);
33911816cb70SBarry Harding 
33921816cb70SBarry Harding 		/*
33931816cb70SBarry Harding 		 * This should be the first thing after the sleep so if we are
33941816cb70SBarry Harding 		 * requested to exit we do that and not waste time doing work
33951816cb70SBarry Harding 		 * we will then abandone.
33961816cb70SBarry Harding 		 */
33971816cb70SBarry Harding 		if (flags & YGE_TASK_EXIT)
33981816cb70SBarry Harding 			break;
33991816cb70SBarry Harding 
34001816cb70SBarry Harding 		/* all processing done without holding locks */
34011816cb70SBarry Harding 		if (flags & YGE_TASK_RESTART)
34021816cb70SBarry Harding 			yge_restart_task(dev);
34031816cb70SBarry Harding 	}
34041816cb70SBarry Harding }
34051816cb70SBarry Harding 
34061816cb70SBarry Harding void
34071816cb70SBarry Harding yge_error(yge_dev_t *dev, yge_port_t *port, char *fmt, ...)
34081816cb70SBarry Harding {
34091816cb70SBarry Harding 	va_list		ap;
34101816cb70SBarry Harding 	char		buf[256];
3411388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 	int		ppa;
34121816cb70SBarry Harding 
34131816cb70SBarry Harding 	va_start(ap, fmt);
34141816cb70SBarry Harding 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
34151816cb70SBarry Harding 	va_end(ap);
34161816cb70SBarry Harding 
3417388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 	if (dev == NULL && port == NULL) {
3418388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 		cmn_err(CE_WARN, "yge: %s", buf);
3419388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 	} else {
3420388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 		if (port != NULL)
3421388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 			ppa = port->p_ppa;
3422388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 		else
3423388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 			ppa = ddi_get_instance(dev->d_dip);
3424388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 		cmn_err(CE_WARN, "yge%d: %s", ppa, buf);
3425388fc72dSvitezslav batrla - Sun Microsystems - Prague Czech Republic 	}
34261816cb70SBarry Harding }
34271816cb70SBarry Harding 
34281816cb70SBarry Harding static int
34291816cb70SBarry Harding yge_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
34301816cb70SBarry Harding {
34311816cb70SBarry Harding 	yge_dev_t	*dev;
34321816cb70SBarry Harding 	int		rv;
34331816cb70SBarry Harding 
34341816cb70SBarry Harding 	switch (cmd) {
34351816cb70SBarry Harding 	case DDI_ATTACH:
34361816cb70SBarry Harding 		dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
34371816cb70SBarry Harding 		dev->d_port[0] = kmem_zalloc(sizeof (yge_port_t), KM_SLEEP);
34381816cb70SBarry Harding 		dev->d_port[1] = kmem_zalloc(sizeof (yge_port_t), KM_SLEEP);
34391816cb70SBarry Harding 		dev->d_dip = dip;
34401816cb70SBarry Harding 		ddi_set_driver_private(dip, dev);
34411816cb70SBarry Harding 
34421816cb70SBarry Harding 		dev->d_port[0]->p_port = 0;
34431816cb70SBarry Harding 		dev->d_port[0]->p_dev = dev;
34441816cb70SBarry Harding 		dev->d_port[1]->p_port = 0;
34451816cb70SBarry Harding 		dev->d_port[1]->p_dev = dev;
34461816cb70SBarry Harding 
34471816cb70SBarry Harding 		rv = yge_attach(dev);
34481816cb70SBarry Harding 		if (rv != DDI_SUCCESS) {
34491816cb70SBarry Harding 			ddi_set_driver_private(dip, 0);
34501816cb70SBarry Harding 			kmem_free(dev->d_port[1], sizeof (yge_port_t));
34511816cb70SBarry Harding 			kmem_free(dev->d_port[0], sizeof (yge_port_t));
34521816cb70SBarry Harding 			kmem_free(dev, sizeof (*dev));
34531816cb70SBarry Harding 		}
34541816cb70SBarry Harding 		return (rv);
34551816cb70SBarry Harding 
34561816cb70SBarry Harding 	case DDI_RESUME:
34571816cb70SBarry Harding 		dev = ddi_get_driver_private(dip);
34581816cb70SBarry Harding 		ASSERT(dev != NULL);
34591816cb70SBarry Harding 		return (yge_resume(dev));
34601816cb70SBarry Harding 
34611816cb70SBarry Harding 	default:
34621816cb70SBarry Harding 		return (DDI_FAILURE);
34631816cb70SBarry Harding 	}
34641816cb70SBarry Harding }
34651816cb70SBarry Harding 
34661816cb70SBarry Harding static int
34671816cb70SBarry Harding yge_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
34681816cb70SBarry Harding {
34691816cb70SBarry Harding 	yge_dev_t	*dev;
3470*3661c01dSGarrett D'Amore 	mac_handle_t	mh;
34711816cb70SBarry Harding 
34721816cb70SBarry Harding 	switch (cmd) {
34731816cb70SBarry Harding 	case DDI_DETACH:
34741816cb70SBarry Harding 
34751816cb70SBarry Harding 		dev = ddi_get_driver_private(dip);
34761816cb70SBarry Harding 
34771816cb70SBarry Harding 		/* attempt to unregister MACs from Nemo */
34781816cb70SBarry Harding 		for (int i = 0; i < dev->d_num_port; i++) {
3479*3661c01dSGarrett D'Amore 
3480*3661c01dSGarrett D'Amore 			if (((mh = dev->d_port[i]->p_mh) != NULL) &&
3481*3661c01dSGarrett D'Amore 			    (mac_disable(mh) != 0)) {
3482*3661c01dSGarrett D'Amore 				/*
3483*3661c01dSGarrett D'Amore 				 * We'd really like a mac_enable to reenable
3484*3661c01dSGarrett D'Amore 				 * any MACs that we previously disabled.  Too
3485*3661c01dSGarrett D'Amore 				 * bad GLDv3 doesn't have one.
3486*3661c01dSGarrett D'Amore 				 */
34871816cb70SBarry Harding 				return (DDI_FAILURE);
34881816cb70SBarry Harding 			}
34891816cb70SBarry Harding 		}
34901816cb70SBarry Harding 
34911816cb70SBarry Harding 		ASSERT(dip == dev->d_dip);
34921816cb70SBarry Harding 		yge_detach(dev);
34931816cb70SBarry Harding 		ddi_set_driver_private(dip, 0);
3494*3661c01dSGarrett D'Amore 		for (int i = 0; i < dev->d_num_port; i++) {
3495*3661c01dSGarrett D'Amore 			if ((mh = dev->d_port[i]->p_mh) != NULL) {
3496*3661c01dSGarrett D'Amore 				/* This can't fail after mac_disable above. */
3497*3661c01dSGarrett D'Amore 				(void) mac_unregister(mh);
3498*3661c01dSGarrett D'Amore 			}
3499*3661c01dSGarrett D'Amore 		}
35001816cb70SBarry Harding 		kmem_free(dev->d_port[1], sizeof (yge_port_t));
35011816cb70SBarry Harding 		kmem_free(dev->d_port[0], sizeof (yge_port_t));
35021816cb70SBarry Harding 		kmem_free(dev, sizeof (*dev));
35031816cb70SBarry Harding 		return (DDI_SUCCESS);
35041816cb70SBarry Harding 
35051816cb70SBarry Harding 	case DDI_SUSPEND:
35061816cb70SBarry Harding 		dev = ddi_get_driver_private(dip);
35071816cb70SBarry Harding 		ASSERT(dev != NULL);
35081816cb70SBarry Harding 		return (yge_suspend(dev));
35091816cb70SBarry Harding 
35101816cb70SBarry Harding 	default:
35111816cb70SBarry Harding 		return (DDI_FAILURE);
35121816cb70SBarry Harding 	}
35131816cb70SBarry Harding }
35141816cb70SBarry Harding 
35151816cb70SBarry Harding static int
35161816cb70SBarry Harding yge_quiesce(dev_info_t *dip)
35171816cb70SBarry Harding {
35181816cb70SBarry Harding 	yge_dev_t *dev;
35191816cb70SBarry Harding 
35201816cb70SBarry Harding 	dev = ddi_get_driver_private(dip);
35211816cb70SBarry Harding 	ASSERT(dev != NULL);
35221816cb70SBarry Harding 
35231816cb70SBarry Harding 	/* NB: No locking!  We are called in single threaded context */
35241816cb70SBarry Harding 	for (int i = 0; i < dev->d_num_port; i++) {
35251816cb70SBarry Harding 		yge_port_t *port = dev->d_port[i];
35261816cb70SBarry Harding 		if (port->p_running)
35271816cb70SBarry Harding 			yge_stop_port(port);
35281816cb70SBarry Harding 	}
35291816cb70SBarry Harding 
35301816cb70SBarry Harding 	/* Disable all interrupts. */
35311816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_IMSK, 0);
35321816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_IMSK);
35331816cb70SBarry Harding 	CSR_WRITE_4(dev, B0_HWE_IMSK, 0);
35341816cb70SBarry Harding 	(void) CSR_READ_4(dev, B0_HWE_IMSK);
35351816cb70SBarry Harding 
35361816cb70SBarry Harding 	/* Put hardware into reset. */
35371816cb70SBarry Harding 	CSR_WRITE_2(dev, B0_CTST, CS_RST_SET);
35381816cb70SBarry Harding 
35391816cb70SBarry Harding 	return (DDI_SUCCESS);
35401816cb70SBarry Harding }
35411816cb70SBarry Harding 
35421816cb70SBarry Harding /*
35431816cb70SBarry Harding  * Stream information
35441816cb70SBarry Harding  */
35451816cb70SBarry Harding DDI_DEFINE_STREAM_OPS(yge_devops, nulldev, nulldev, yge_ddi_attach,
35461816cb70SBarry Harding     yge_ddi_detach, nodev, NULL, D_MP, NULL, yge_quiesce);
35471816cb70SBarry Harding 
35481816cb70SBarry Harding /*
35491816cb70SBarry Harding  * Module linkage information.
35501816cb70SBarry Harding  */
35511816cb70SBarry Harding 
35521816cb70SBarry Harding static struct modldrv yge_modldrv = {
35531816cb70SBarry Harding 	&mod_driverops,			/* drv_modops */
35541816cb70SBarry Harding 	"Yukon 2 Ethernet",		/* drv_linkinfo */
35551816cb70SBarry Harding 	&yge_devops			/* drv_dev_ops */
35561816cb70SBarry Harding };
35571816cb70SBarry Harding 
35581816cb70SBarry Harding static struct modlinkage yge_modlinkage = {
35591816cb70SBarry Harding 	MODREV_1,		/* ml_rev */
35601816cb70SBarry Harding 	&yge_modldrv,		/* ml_linkage */
35611816cb70SBarry Harding 	NULL
35621816cb70SBarry Harding };
35631816cb70SBarry Harding 
35641816cb70SBarry Harding /*
35651816cb70SBarry Harding  * DDI entry points.
35661816cb70SBarry Harding  */
35671816cb70SBarry Harding int
35681816cb70SBarry Harding _init(void)
35691816cb70SBarry Harding {
35701816cb70SBarry Harding 	int	rv;
35711816cb70SBarry Harding 	mac_init_ops(&yge_devops, "yge");
35721816cb70SBarry Harding 	if ((rv = mod_install(&yge_modlinkage)) != DDI_SUCCESS) {
35731816cb70SBarry Harding 		mac_fini_ops(&yge_devops);
35741816cb70SBarry Harding 	}
35751816cb70SBarry Harding 	return (rv);
35761816cb70SBarry Harding }
35771816cb70SBarry Harding 
35781816cb70SBarry Harding int
35791816cb70SBarry Harding _fini(void)
35801816cb70SBarry Harding {
35811816cb70SBarry Harding 	int	rv;
35821816cb70SBarry Harding 	if ((rv = mod_remove(&yge_modlinkage)) == DDI_SUCCESS) {
35831816cb70SBarry Harding 		mac_fini_ops(&yge_devops);
35841816cb70SBarry Harding 	}
35851816cb70SBarry Harding 	return (rv);
35861816cb70SBarry Harding }
35871816cb70SBarry Harding 
35881816cb70SBarry Harding int
35891816cb70SBarry Harding _info(struct modinfo *modinfop)
35901816cb70SBarry Harding {
35911816cb70SBarry Harding 	return (mod_info(&yge_modlinkage, modinfop));
35921816cb70SBarry Harding }
3593