xref: /onnv-gate/usr/src/uts/common/io/afe/afe.c (revision 11878:ac93462db6d7)
14992Sgd78059 /*
24992Sgd78059  * Solaris driver for ethernet cards based on the ADMtek Centaur
34992Sgd78059  *
44992Sgd78059  * Copyright (c) 2007 by Garrett D'Amore <garrett@damore.org>.
54992Sgd78059  * All rights reserved.
64992Sgd78059  *
74992Sgd78059  * Redistribution and use in source and binary forms, with or without
84992Sgd78059  * modification, are permitted provided that the following conditions
94992Sgd78059  * are met:
104992Sgd78059  * 1. Redistributions of source code must retain the above copyright
114992Sgd78059  *    notice, this list of conditions and the following disclaimer.
124992Sgd78059  * 2. Redistributions in binary form must reproduce the above copyright
134992Sgd78059  *    notice, this list of conditions and the following disclaimer in the
144992Sgd78059  *    documentation and/or other materials provided with the distribution.
154992Sgd78059  * 3. Neither the name of the author nor the names of any co-contributors
164992Sgd78059  *    may be used to endorse or promote products derived from this software
174992Sgd78059  *    without specific prior written permission.
184992Sgd78059  *
194992Sgd78059  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
204992Sgd78059  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
214992Sgd78059  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
224992Sgd78059  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
234992Sgd78059  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
244992Sgd78059  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
254992Sgd78059  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
264992Sgd78059  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
274992Sgd78059  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
284992Sgd78059  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
294992Sgd78059  * POSSIBILITY OF SUCH DAMAGE.
304992Sgd78059  */
316684Sgd78059 /*
32*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
336684Sgd78059  * Use is subject to license terms.
346684Sgd78059  */
354992Sgd78059 
364992Sgd78059 
374992Sgd78059 #include <sys/varargs.h>
384992Sgd78059 #include <sys/types.h>
394992Sgd78059 #include <sys/modctl.h>
404992Sgd78059 #include <sys/conf.h>
414992Sgd78059 #include <sys/devops.h>
424992Sgd78059 #include <sys/stream.h>
434992Sgd78059 #include <sys/strsun.h>
444992Sgd78059 #include <sys/cmn_err.h>
454992Sgd78059 #include <sys/ethernet.h>
464992Sgd78059 #include <sys/kmem.h>
474992Sgd78059 #include <sys/time.h>
484992Sgd78059 #include <sys/crc32.h>
499860Sgdamore@opensolaris.org #include <sys/mii.h>
504992Sgd78059 #include <sys/miiregs.h>
514992Sgd78059 #include <sys/mac.h>
524992Sgd78059 #include <sys/mac_ether.h>
534992Sgd78059 #include <sys/ddi.h>
544992Sgd78059 #include <sys/sunddi.h>
555895Syz147064 #include <sys/vlan.h>
564992Sgd78059 
574992Sgd78059 #include "afe.h"
584992Sgd78059 #include "afeimpl.h"
594992Sgd78059 
604992Sgd78059 /*
614992Sgd78059  * Driver globals.
624992Sgd78059  */
634992Sgd78059 
644992Sgd78059 /* table of supported devices */
654992Sgd78059 static afe_card_t afe_cards[] = {
664992Sgd78059 
674992Sgd78059 	/*
684992Sgd78059 	 * ADMtek Centaur and Comet
694992Sgd78059 	 */
704992Sgd78059 	{ 0x1317, 0x0981, "ADMtek AL981", MODEL_COMET },
714992Sgd78059 	{ 0x1317, 0x0985, "ADMtek AN983", MODEL_CENTAUR },
724992Sgd78059 	{ 0x1317, 0x1985, "ADMtek AN985", MODEL_CENTAUR },
734992Sgd78059 	{ 0x1317, 0x9511, "ADMtek ADM9511", MODEL_CENTAUR },
744992Sgd78059 	{ 0x1317, 0x9513, "ADMtek ADM9513", MODEL_CENTAUR },
754992Sgd78059 	/*
764992Sgd78059 	 * Accton just relabels other companies' controllers
774992Sgd78059 	 */
784992Sgd78059 	{ 0x1113, 0x1216, "Accton EN5251", MODEL_CENTAUR },
794992Sgd78059 	/*
804992Sgd78059 	 * Models listed here.
814992Sgd78059 	 */
824992Sgd78059 	{ 0x10b7, 0x9300, "3Com 3CSOHO100B-TX", MODEL_CENTAUR },
834992Sgd78059 	{ 0x1113, 0xec02, "SMC SMC1244TX", MODEL_CENTAUR },
844992Sgd78059 	{ 0x10b8, 0x1255, "SMC SMC1255TX", MODEL_CENTAUR },
854992Sgd78059 	{ 0x111a, 0x1020, "Siemens SpeedStream PCI 10/100", MODEL_CENTAUR },
864992Sgd78059 	{ 0x1113, 0x1207, "Accton EN1207F", MODEL_CENTAUR },
874992Sgd78059 	{ 0x1113, 0x2242, "Accton EN2242", MODEL_CENTAUR },
884992Sgd78059 	{ 0x1113, 0x2220, "Accton EN2220", MODEL_CENTAUR },
894992Sgd78059 	{ 0x1113, 0x9216, "3M VOL-N100VF+TX", MODEL_CENTAUR },
904992Sgd78059 	{ 0x1317, 0x0574, "Linksys LNE100TX", MODEL_CENTAUR },
914992Sgd78059 	{ 0x1317, 0x0570, "Linksys NC100", MODEL_CENTAUR },
924992Sgd78059 	{ 0x1385, 0x511a, "Netgear FA511", MODEL_CENTAUR },
934992Sgd78059 	{ 0x13d1, 0xab02, "AboCom FE2500", MODEL_CENTAUR },
944992Sgd78059 	{ 0x13d1, 0xab03, "AboCom PCM200", MODEL_CENTAUR },
954992Sgd78059 	{ 0x13d1, 0xab08, "AboCom FE2500MX", MODEL_CENTAUR },
964992Sgd78059 	{ 0x1414, 0x0001, "Microsoft MN-120", MODEL_CENTAUR },
974992Sgd78059 	{ 0x16ec, 0x00ed, "U.S. Robotics USR997900", MODEL_CENTAUR },
984992Sgd78059 	{ 0x1734, 0x100c, "Fujitsu-Siemens D1961", MODEL_CENTAUR },
994992Sgd78059 	{ 0x1737, 0xab08, "Linksys PCMPC200", MODEL_CENTAUR },
1004992Sgd78059 	{ 0x1737, 0xab09, "Linksys PCM200", MODEL_CENTAUR },
1014992Sgd78059 	{ 0x17b3, 0xab08, "Hawking PN672TX", MODEL_CENTAUR },
1024992Sgd78059 };
1034992Sgd78059 
1044992Sgd78059 #define	ETHERVLANMTU	(ETHERMAX + 4)
1054992Sgd78059 
1064992Sgd78059 /*
1074992Sgd78059  * Function prototypes
1084992Sgd78059  */
1094992Sgd78059 static int	afe_attach(dev_info_t *, ddi_attach_cmd_t);
1104992Sgd78059 static int	afe_detach(dev_info_t *, ddi_detach_cmd_t);
1114992Sgd78059 static int	afe_resume(dev_info_t *);
1129315Sgdamore@opensolaris.org static int	afe_quiesce(dev_info_t *);
1134992Sgd78059 static int	afe_m_unicst(void *, const uint8_t *);
1144992Sgd78059 static int	afe_m_multicst(void *, boolean_t, const uint8_t *);
1154992Sgd78059 static int	afe_m_promisc(void *, boolean_t);
1164992Sgd78059 static mblk_t	*afe_m_tx(void *, mblk_t *);
1179860Sgdamore@opensolaris.org static void	afe_m_ioctl(void *, queue_t *, mblk_t *);
1184992Sgd78059 static int	afe_m_stat(void *, uint_t, uint64_t *);
1194992Sgd78059 static int	afe_m_start(void *);
1204992Sgd78059 static void	afe_m_stop(void *);
1216684Sgd78059 static int	afe_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
122*11878SVenu.Iyer@Sun.COM     void *);
1236684Sgd78059 static int	afe_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
1246684Sgd78059     const void *);
125*11878SVenu.Iyer@Sun.COM static void	afe_m_propinfo(void *, const char *, mac_prop_id_t,
126*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
1274992Sgd78059 static unsigned	afe_intr(caddr_t);
1284992Sgd78059 static void	afe_startmac(afe_t *);
1294992Sgd78059 static void	afe_stopmac(afe_t *);
1304992Sgd78059 static void	afe_resetrings(afe_t *);
1314992Sgd78059 static boolean_t	afe_initialize(afe_t *);
1324992Sgd78059 static void	afe_startall(afe_t *);
1334992Sgd78059 static void	afe_stopall(afe_t *);
1344992Sgd78059 static void	afe_resetall(afe_t *);
1354992Sgd78059 static afe_txbuf_t *afe_alloctxbuf(afe_t *);
1364992Sgd78059 static void	afe_destroytxbuf(afe_txbuf_t *);
1374992Sgd78059 static afe_rxbuf_t *afe_allocrxbuf(afe_t *);
1384992Sgd78059 static void	afe_destroyrxbuf(afe_rxbuf_t *);
1394992Sgd78059 static boolean_t	afe_send(afe_t *, mblk_t *);
1404992Sgd78059 static int	afe_allocrxring(afe_t *);
1414992Sgd78059 static void	afe_freerxring(afe_t *);
1424992Sgd78059 static int	afe_alloctxring(afe_t *);
1434992Sgd78059 static void	afe_freetxring(afe_t *);
1444992Sgd78059 static void	afe_error(dev_info_t *, char *, ...);
1454992Sgd78059 static void	afe_setrxfilt(afe_t *);
1469860Sgdamore@opensolaris.org static int	afe_watchdog(afe_t *);
1474992Sgd78059 static uint8_t	afe_sromwidth(afe_t *);
1484992Sgd78059 static uint16_t	afe_readsromword(afe_t *, unsigned);
1494992Sgd78059 static void	afe_readsrom(afe_t *, unsigned, unsigned, char *);
1504992Sgd78059 static void	afe_getfactaddr(afe_t *, uchar_t *);
1516684Sgd78059 static uint8_t	afe_miireadbit(afe_t *);
1526684Sgd78059 static void	afe_miiwritebit(afe_t *, uint8_t);
1534992Sgd78059 static void	afe_miitristate(afe_t *);
1549860Sgdamore@opensolaris.org static uint16_t	afe_miireadgeneral(afe_t *, uint8_t, uint8_t);
1559860Sgdamore@opensolaris.org static void	afe_miiwritegeneral(afe_t *, uint8_t, uint8_t, uint16_t);
1569860Sgdamore@opensolaris.org static uint16_t	afe_miireadcomet(afe_t *, uint8_t, uint8_t);
1579860Sgdamore@opensolaris.org static void	afe_miiwritecomet(afe_t *, uint8_t, uint8_t, uint16_t);
1589860Sgdamore@opensolaris.org static uint16_t	afe_mii_read(void *, uint8_t, uint8_t);
1599860Sgdamore@opensolaris.org static void	afe_mii_write(void *, uint8_t, uint8_t, uint16_t);
1609860Sgdamore@opensolaris.org static void	afe_mii_notify(void *, link_state_t);
1619860Sgdamore@opensolaris.org static void	afe_mii_reset(void *);
1624992Sgd78059 static void	afe_disableinterrupts(afe_t *);
1634992Sgd78059 static void	afe_enableinterrupts(afe_t *);
1644992Sgd78059 static void	afe_reclaim(afe_t *);
1654992Sgd78059 static mblk_t	*afe_receive(afe_t *);
1664992Sgd78059 
1674992Sgd78059 #define	KIOIP	KSTAT_INTR_PTR(afep->afe_intrstat)
1684992Sgd78059 
1699860Sgdamore@opensolaris.org static mii_ops_t afe_mii_ops = {
1709860Sgdamore@opensolaris.org 	MII_OPS_VERSION,
1719860Sgdamore@opensolaris.org 	afe_mii_read,
1729860Sgdamore@opensolaris.org 	afe_mii_write,
1739860Sgdamore@opensolaris.org 	afe_mii_notify,
1749860Sgdamore@opensolaris.org 	afe_mii_reset
1759860Sgdamore@opensolaris.org };
1769860Sgdamore@opensolaris.org 
1774992Sgd78059 static mac_callbacks_t afe_m_callbacks = {
178*11878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
1794992Sgd78059 	afe_m_stat,
1804992Sgd78059 	afe_m_start,
1814992Sgd78059 	afe_m_stop,
1824992Sgd78059 	afe_m_promisc,
1834992Sgd78059 	afe_m_multicst,
1844992Sgd78059 	afe_m_unicst,
1854992Sgd78059 	afe_m_tx,
186*11878SVenu.Iyer@Sun.COM 	NULL,
1879860Sgdamore@opensolaris.org 	afe_m_ioctl,	/* mc_ioctl */
1886684Sgd78059 	NULL,		/* mc_getcapab */
1896684Sgd78059 	NULL,		/* mc_open */
1906684Sgd78059 	NULL,		/* mc_close */
1916684Sgd78059 	afe_m_setprop,
1926684Sgd78059 	afe_m_getprop,
193*11878SVenu.Iyer@Sun.COM 	afe_m_propinfo
1944992Sgd78059 };
1954992Sgd78059 
1964992Sgd78059 
1974992Sgd78059 /*
1984992Sgd78059  * Stream information
1994992Sgd78059  */
2004992Sgd78059 DDI_DEFINE_STREAM_OPS(afe_devops, nulldev, nulldev, afe_attach, afe_detach,
2019315Sgdamore@opensolaris.org     nodev, NULL, D_MP, NULL, afe_quiesce);
2024992Sgd78059 
2034992Sgd78059 /*
2044992Sgd78059  * Module linkage information.
2054992Sgd78059  */
2064992Sgd78059 
2074992Sgd78059 static struct modldrv afe_modldrv = {
2084992Sgd78059 	&mod_driverops,			/* drv_modops */
2094992Sgd78059 	"ADMtek Fast Ethernet",		/* drv_linkinfo */
2104992Sgd78059 	&afe_devops			/* drv_dev_ops */
2114992Sgd78059 };
2124992Sgd78059 
2134992Sgd78059 static struct modlinkage afe_modlinkage = {
2144992Sgd78059 	MODREV_1,		/* ml_rev */
2154992Sgd78059 	{ &afe_modldrv, NULL }	/* ml_linkage */
2164992Sgd78059 };
2174992Sgd78059 
2184992Sgd78059 /*
2194992Sgd78059  * Device attributes.
2204992Sgd78059  */
2214992Sgd78059 static ddi_device_acc_attr_t afe_devattr = {
2224992Sgd78059 	DDI_DEVICE_ATTR_V0,
2234992Sgd78059 	DDI_STRUCTURE_LE_ACC,
2244992Sgd78059 	DDI_STRICTORDER_ACC
2254992Sgd78059 };
2264992Sgd78059 
2274992Sgd78059 static ddi_device_acc_attr_t afe_bufattr = {
2284992Sgd78059 	DDI_DEVICE_ATTR_V0,
2294992Sgd78059 	DDI_NEVERSWAP_ACC,
2304992Sgd78059 	DDI_STRICTORDER_ACC
2314992Sgd78059 };
2324992Sgd78059 
2334992Sgd78059 static ddi_dma_attr_t afe_dma_attr = {
2344992Sgd78059 	DMA_ATTR_V0,		/* dma_attr_version */
2354992Sgd78059 	0,			/* dma_attr_addr_lo */
2364992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
2374992Sgd78059 	0x7FFFFFFFU,		/* dma_attr_count_max */
2384992Sgd78059 	4,			/* dma_attr_align */
2394992Sgd78059 	0x3F,			/* dma_attr_burstsizes */
2404992Sgd78059 	1,			/* dma_attr_minxfer */
2414992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
2424992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_seg */
2434992Sgd78059 	1,			/* dma_attr_sgllen */
2444992Sgd78059 	1,			/* dma_attr_granular */
2454992Sgd78059 	0			/* dma_attr_flags */
2464992Sgd78059 };
2474992Sgd78059 
2484992Sgd78059 /*
2494992Sgd78059  * Tx buffers can be arbitrarily aligned.  Additionally, they can
2504992Sgd78059  * cross a page boundary, so we use the two buffer addresses of the
2514992Sgd78059  * chip to provide a two-entry scatter-gather list.
2524992Sgd78059  */
2534992Sgd78059 static ddi_dma_attr_t afe_dma_txattr = {
2544992Sgd78059 	DMA_ATTR_V0,		/* dma_attr_version */
2554992Sgd78059 	0,			/* dma_attr_addr_lo */
2564992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
2574992Sgd78059 	0x7FFFFFFFU,		/* dma_attr_count_max */
2584992Sgd78059 	1,			/* dma_attr_align */
2594992Sgd78059 	0x3F,			/* dma_attr_burstsizes */
2604992Sgd78059 	1,			/* dma_attr_minxfer */
2614992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
2624992Sgd78059 	0xFFFFFFFFU,		/* dma_attr_seg */
2634992Sgd78059 	2,			/* dma_attr_sgllen */
2644992Sgd78059 	1,			/* dma_attr_granular */
2654992Sgd78059 	0			/* dma_attr_flags */
2664992Sgd78059 };
2674992Sgd78059 
2684992Sgd78059 /*
2694992Sgd78059  * Ethernet addresses.
2704992Sgd78059  */
2714992Sgd78059 static uchar_t afe_broadcast[ETHERADDRL] = {
2724992Sgd78059 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2734992Sgd78059 };
2744992Sgd78059 
2754992Sgd78059 /*
2764992Sgd78059  * DDI entry points.
2774992Sgd78059  */
2784992Sgd78059 int
_init(void)2794992Sgd78059 _init(void)
2804992Sgd78059 {
2814992Sgd78059 	int	rv;
2824992Sgd78059 	mac_init_ops(&afe_devops, "afe");
2834992Sgd78059 	if ((rv = mod_install(&afe_modlinkage)) != DDI_SUCCESS) {
2844992Sgd78059 		mac_fini_ops(&afe_devops);
2854992Sgd78059 	}
2864992Sgd78059 	return (rv);
2874992Sgd78059 }
2884992Sgd78059 
2894992Sgd78059 int
_fini(void)2904992Sgd78059 _fini(void)
2914992Sgd78059 {
2924992Sgd78059 	int	rv;
2934992Sgd78059 	if ((rv = mod_remove(&afe_modlinkage)) == DDI_SUCCESS) {
2944992Sgd78059 		mac_fini_ops(&afe_devops);
2954992Sgd78059 	}
2964992Sgd78059 	return (rv);
2974992Sgd78059 }
2984992Sgd78059 
2994992Sgd78059 int
_info(struct modinfo * modinfop)3004992Sgd78059 _info(struct modinfo *modinfop)
3014992Sgd78059 {
3024992Sgd78059 	return (mod_info(&afe_modlinkage, modinfop));
3034992Sgd78059 }
3044992Sgd78059 
3054992Sgd78059 int
afe_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3064992Sgd78059 afe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3074992Sgd78059 {
3084992Sgd78059 	afe_t			*afep;
3094992Sgd78059 	mac_register_t		*macp;
3104992Sgd78059 	int			inst = ddi_get_instance(dip);
3114992Sgd78059 	ddi_acc_handle_t	pci;
3124992Sgd78059 	uint16_t		venid;
3134992Sgd78059 	uint16_t		devid;
3144992Sgd78059 	uint16_t		svid;
3154992Sgd78059 	uint16_t		ssid;
3164992Sgd78059 	uint16_t		cachesize;
3174992Sgd78059 	afe_card_t		*cardp;
3184992Sgd78059 	int			i;
3194992Sgd78059 
3204992Sgd78059 	switch (cmd) {
3214992Sgd78059 	case DDI_RESUME:
3224992Sgd78059 		return (afe_resume(dip));
3234992Sgd78059 
3244992Sgd78059 	case DDI_ATTACH:
3254992Sgd78059 		break;
3264992Sgd78059 
3274992Sgd78059 	default:
3284992Sgd78059 		return (DDI_FAILURE);
3294992Sgd78059 	}
3304992Sgd78059 
3314992Sgd78059 	/* this card is a bus master, reject any slave-only slot */
3324992Sgd78059 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
3334992Sgd78059 		afe_error(dip, "slot does not support PCI bus-master");
3344992Sgd78059 		return (DDI_FAILURE);
3354992Sgd78059 	}
3364992Sgd78059 	/* PCI devices shouldn't generate hilevel interrupts */
3374992Sgd78059 	if (ddi_intr_hilevel(dip, 0) != 0) {
3384992Sgd78059 		afe_error(dip, "hilevel interrupts not supported");
3394992Sgd78059 		return (DDI_FAILURE);
3404992Sgd78059 	}
3414992Sgd78059 	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
3424992Sgd78059 		afe_error(dip, "unable to setup PCI config handle");
3434992Sgd78059 		return (DDI_FAILURE);
3444992Sgd78059 	}
3454992Sgd78059 
3464992Sgd78059 	venid = pci_config_get16(pci, PCI_VID);
3474992Sgd78059 	devid = pci_config_get16(pci, PCI_DID);
3484992Sgd78059 	svid = pci_config_get16(pci, PCI_SVID);
3494992Sgd78059 	ssid = pci_config_get16(pci, PCI_SSID);
3504992Sgd78059 
3514992Sgd78059 	/*
3524992Sgd78059 	 * Note: ADMtek boards seem to misprogram themselves with bogus
3534992Sgd78059 	 * timings, which do not seem to work properly on SPARC.  We
3544992Sgd78059 	 * reprogram them zero (but only if they appear to be broken),
3554992Sgd78059 	 * which seems to at least work.  Its unclear that this is a
3564992Sgd78059 	 * legal or wise practice to me, but it certainly works better
3574992Sgd78059 	 * than the original values.  (I would love to hear
3584992Sgd78059 	 * suggestions for better values, or a better strategy.)
3594992Sgd78059 	 */
3604992Sgd78059 	if ((pci_config_get8(pci, PCI_MINGNT) == 0xff) &&
3614992Sgd78059 	    (pci_config_get8(pci, PCI_MAXLAT) == 0xff)) {
3624992Sgd78059 		pci_config_put8(pci, PCI_MINGNT, 0);
3634992Sgd78059 		pci_config_put8(pci, PCI_MAXLAT, 0);
3644992Sgd78059 	}
3654992Sgd78059 
3664992Sgd78059 	/*
3674992Sgd78059 	 * the last entry in the card table matches every possible
3684992Sgd78059 	 * card, so the for-loop always terminates properly.
3694992Sgd78059 	 */
3704992Sgd78059 	cardp = NULL;
3714992Sgd78059 	for (i = 0; i < (sizeof (afe_cards) / sizeof (afe_card_t)); i++) {
3724992Sgd78059 		if ((venid == afe_cards[i].card_venid) &&
3734992Sgd78059 		    (devid == afe_cards[i].card_devid)) {
3744992Sgd78059 			cardp = &afe_cards[i];
3754992Sgd78059 		}
3764992Sgd78059 		if ((svid == afe_cards[i].card_venid) &&
3774992Sgd78059 		    (ssid == afe_cards[i].card_devid)) {
3784992Sgd78059 			cardp = &afe_cards[i];
3794992Sgd78059 			break;
3804992Sgd78059 		}
3814992Sgd78059 	}
3824992Sgd78059 
3834992Sgd78059 	if (cardp == NULL) {
3844992Sgd78059 		pci_config_teardown(&pci);
3854992Sgd78059 		afe_error(dip, "Unable to identify PCI card");
3864992Sgd78059 		return (DDI_FAILURE);
3874992Sgd78059 	}
3884992Sgd78059 
3894992Sgd78059 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
3904992Sgd78059 	    cardp->card_cardname) != DDI_PROP_SUCCESS) {
3914992Sgd78059 		pci_config_teardown(&pci);
3924992Sgd78059 		afe_error(dip, "Unable to create model property");
3934992Sgd78059 		return (DDI_FAILURE);
3944992Sgd78059 	}
3954992Sgd78059 
3964992Sgd78059 	/*
3974992Sgd78059 	 * Grab the PCI cachesize -- we use this to program the
3984992Sgd78059 	 * cache-optimization bus access bits.
3994992Sgd78059 	 */
4004992Sgd78059 	cachesize = pci_config_get8(pci, PCI_CLS);
4014992Sgd78059 
4024992Sgd78059 	/* this cannot fail */
4034992Sgd78059 	afep = kmem_zalloc(sizeof (afe_t), KM_SLEEP);
4044992Sgd78059 	ddi_set_driver_private(dip, afep);
4054992Sgd78059 
4064992Sgd78059 	/* get the interrupt block cookie */
4074992Sgd78059 	if (ddi_get_iblock_cookie(dip, 0, &afep->afe_icookie) != DDI_SUCCESS) {
4084992Sgd78059 		afe_error(dip, "ddi_get_iblock_cookie failed");
4094992Sgd78059 		pci_config_teardown(&pci);
4104992Sgd78059 		kmem_free(afep, sizeof (afe_t));
4114992Sgd78059 		return (DDI_FAILURE);
4124992Sgd78059 	}
4134992Sgd78059 
4144992Sgd78059 	afep->afe_dip = dip;
4154992Sgd78059 	afep->afe_cardp = cardp;
4164992Sgd78059 	afep->afe_phyaddr = -1;
4174992Sgd78059 	afep->afe_cachesize = cachesize;
4184992Sgd78059 
4194992Sgd78059 	afep->afe_forcefiber = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
4204992Sgd78059 	    "fiber", 0);
4214992Sgd78059 
4224992Sgd78059 	mutex_init(&afep->afe_xmtlock, NULL, MUTEX_DRIVER, afep->afe_icookie);
4234992Sgd78059 	mutex_init(&afep->afe_intrlock, NULL, MUTEX_DRIVER, afep->afe_icookie);
4244992Sgd78059 
4254992Sgd78059 	/*
4264992Sgd78059 	 * Enable bus master, IO space, and memory space accesses.
4274992Sgd78059 	 */
4284992Sgd78059 	pci_config_put16(pci, PCI_CMD,
4294992Sgd78059 	    pci_config_get16(pci, PCI_CMD) | PCI_CMD_BME | PCI_CMD_MAE);
4304992Sgd78059 
4314992Sgd78059 	/* we're done with this now, drop it */
4324992Sgd78059 	pci_config_teardown(&pci);
4334992Sgd78059 
4344992Sgd78059 	/*
4354992Sgd78059 	 * Initialize interrupt kstat.  This should not normally fail, since
4364992Sgd78059 	 * we don't use a persistent stat.  We do it this way to avoid having
4374992Sgd78059 	 * to test for it at run time on the hot path.
4384992Sgd78059 	 */
4394992Sgd78059 	afep->afe_intrstat = kstat_create("afe", inst, "intr", "controller",
4404992Sgd78059 	    KSTAT_TYPE_INTR, 1, 0);
4414992Sgd78059 	if (afep->afe_intrstat == NULL) {
4424992Sgd78059 		afe_error(dip, "kstat_create failed");
4434992Sgd78059 		goto failed;
4444992Sgd78059 	}
4454992Sgd78059 	kstat_install(afep->afe_intrstat);
4464992Sgd78059 
4474992Sgd78059 	/*
4489860Sgdamore@opensolaris.org 	 * Set up the MII.
4499860Sgdamore@opensolaris.org 	 */
4509860Sgdamore@opensolaris.org 	if ((afep->afe_mii = mii_alloc(afep, dip, &afe_mii_ops)) == NULL) {
4519860Sgdamore@opensolaris.org 		goto failed;
4529860Sgdamore@opensolaris.org 	}
4539860Sgdamore@opensolaris.org 
4549860Sgdamore@opensolaris.org 	/*
4559860Sgdamore@opensolaris.org 	 * Centaur can support PAUSE, but Comet can't.
4569860Sgdamore@opensolaris.org 	 */
4579860Sgdamore@opensolaris.org 	if (AFE_MODEL(afep) == MODEL_CENTAUR) {
4589860Sgdamore@opensolaris.org 		mii_set_pauseable(afep->afe_mii, B_TRUE, B_FALSE);
4599860Sgdamore@opensolaris.org 	} else {
4609860Sgdamore@opensolaris.org 		mii_set_pauseable(afep->afe_mii, B_FALSE, B_FALSE);
4619860Sgdamore@opensolaris.org 	}
4629860Sgdamore@opensolaris.org 
4639860Sgdamore@opensolaris.org 	/*
4644992Sgd78059 	 * Map in the device registers.
4654992Sgd78059 	 */
4664992Sgd78059 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&afep->afe_regs,
4674992Sgd78059 	    0, 0, &afe_devattr, &afep->afe_regshandle)) {
4684992Sgd78059 		afe_error(dip, "ddi_regs_map_setup failed");
4694992Sgd78059 		goto failed;
4704992Sgd78059 	}
4714992Sgd78059 
4724992Sgd78059 	/*
4734992Sgd78059 	 * Allocate DMA resources (descriptor rings and buffers).
4744992Sgd78059 	 */
4754992Sgd78059 	if ((afe_allocrxring(afep) != DDI_SUCCESS) ||
4764992Sgd78059 	    (afe_alloctxring(afep) != DDI_SUCCESS)) {
4774992Sgd78059 		afe_error(dip, "unable to allocate DMA resources");
4784992Sgd78059 		goto failed;
4794992Sgd78059 	}
4804992Sgd78059 
4814992Sgd78059 	/* Initialize the chip. */
4824992Sgd78059 	mutex_enter(&afep->afe_intrlock);
4834992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
4844992Sgd78059 	if (!afe_initialize(afep)) {
4854992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
4864992Sgd78059 		mutex_exit(&afep->afe_intrlock);
4874992Sgd78059 		goto failed;
4884992Sgd78059 	}
4894992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
4904992Sgd78059 	mutex_exit(&afep->afe_intrlock);
4914992Sgd78059 
4924992Sgd78059 	/* Determine the number of address bits to our EEPROM. */
4934992Sgd78059 	afep->afe_sromwidth = afe_sromwidth(afep);
4944992Sgd78059 
4954992Sgd78059 	/*
4964992Sgd78059 	 * Get the factory ethernet address.  This becomes the current
4974992Sgd78059 	 * ethernet address (it can be overridden later via ifconfig).
4984992Sgd78059 	 */
4994992Sgd78059 	afe_getfactaddr(afep, afep->afe_curraddr);
5004992Sgd78059 	afep->afe_promisc = B_FALSE;
5014992Sgd78059 
5024992Sgd78059 	/* make sure we add configure the initial filter */
5034992Sgd78059 	(void) afe_m_unicst(afep, afep->afe_curraddr);
5044992Sgd78059 	(void) afe_m_multicst(afep, B_TRUE, afe_broadcast);
5054992Sgd78059 
5064992Sgd78059 	/*
5074992Sgd78059 	 * Establish interrupt handler.
5084992Sgd78059 	 */
5094992Sgd78059 	if (ddi_add_intr(dip, 0, NULL, NULL, afe_intr, (caddr_t)afep) !=
5104992Sgd78059 	    DDI_SUCCESS) {
5114992Sgd78059 		afe_error(dip, "unable to add interrupt");
5124992Sgd78059 		goto failed;
5134992Sgd78059 	}
5144992Sgd78059 
5154992Sgd78059 	/* TODO: do the power management stuff */
5164992Sgd78059 
5174992Sgd78059 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
5184992Sgd78059 		afe_error(dip, "mac_alloc failed");
5194992Sgd78059 		goto failed;
5204992Sgd78059 	}
5214992Sgd78059 
5224992Sgd78059 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
5234992Sgd78059 	macp->m_driver = afep;
5244992Sgd78059 	macp->m_dip = dip;
5254992Sgd78059 	macp->m_src_addr = afep->afe_curraddr;
5264992Sgd78059 	macp->m_callbacks = &afe_m_callbacks;
5274992Sgd78059 	macp->m_min_sdu = 0;
5284992Sgd78059 	macp->m_max_sdu = ETHERMTU;
5295895Syz147064 	macp->m_margin = VLAN_TAGSZ;
5304992Sgd78059 
5314992Sgd78059 	if (mac_register(macp, &afep->afe_mh) == DDI_SUCCESS) {
5324992Sgd78059 		mac_free(macp);
5334992Sgd78059 		return (DDI_SUCCESS);
5344992Sgd78059 	}
5354992Sgd78059 
5364992Sgd78059 	/* failed to register with MAC */
5374992Sgd78059 	mac_free(macp);
5384992Sgd78059 failed:
5394992Sgd78059 	if (afep->afe_icookie != NULL) {
5404992Sgd78059 		ddi_remove_intr(dip, 0, afep->afe_icookie);
5414992Sgd78059 	}
5424992Sgd78059 	if (afep->afe_intrstat) {
5434992Sgd78059 		kstat_delete(afep->afe_intrstat);
5444992Sgd78059 	}
5454992Sgd78059 	mutex_destroy(&afep->afe_intrlock);
5464992Sgd78059 	mutex_destroy(&afep->afe_xmtlock);
5474992Sgd78059 
5484992Sgd78059 	afe_freerxring(afep);
5494992Sgd78059 	afe_freetxring(afep);
5504992Sgd78059 
5514992Sgd78059 	if (afep->afe_regshandle != NULL) {
5524992Sgd78059 		ddi_regs_map_free(&afep->afe_regshandle);
5534992Sgd78059 	}
5544992Sgd78059 	kmem_free(afep, sizeof (afe_t));
5554992Sgd78059 	return (DDI_FAILURE);
5564992Sgd78059 }
5574992Sgd78059 
5584992Sgd78059 int
afe_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5594992Sgd78059 afe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5604992Sgd78059 {
5614992Sgd78059 	afe_t		*afep;
5624992Sgd78059 
5634992Sgd78059 	afep = ddi_get_driver_private(dip);
5644992Sgd78059 	if (afep == NULL) {
5654992Sgd78059 		afe_error(dip, "no soft state in detach!");
5664992Sgd78059 		return (DDI_FAILURE);
5674992Sgd78059 	}
5684992Sgd78059 
5694992Sgd78059 	switch (cmd) {
5704992Sgd78059 	case DDI_DETACH:
5714992Sgd78059 
5724992Sgd78059 		if (mac_unregister(afep->afe_mh) != 0) {
5734992Sgd78059 			return (DDI_FAILURE);
5744992Sgd78059 		}
5754992Sgd78059 
5764992Sgd78059 		/* make sure hardware is quiesced */
5774992Sgd78059 		mutex_enter(&afep->afe_intrlock);
5784992Sgd78059 		mutex_enter(&afep->afe_xmtlock);
5794992Sgd78059 		afep->afe_flags &= ~AFE_RUNNING;
5804992Sgd78059 		afe_stopall(afep);
5814992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
5824992Sgd78059 		mutex_exit(&afep->afe_intrlock);
5834992Sgd78059 
5844992Sgd78059 		/* clean up and shut down device */
5854992Sgd78059 		ddi_remove_intr(dip, 0, afep->afe_icookie);
5864992Sgd78059 
5879860Sgdamore@opensolaris.org 		/* clean up MII layer */
5889860Sgdamore@opensolaris.org 		mii_free(afep->afe_mii);
5899860Sgdamore@opensolaris.org 
5904992Sgd78059 		/* clean up kstats */
5914992Sgd78059 		kstat_delete(afep->afe_intrstat);
5924992Sgd78059 
5934992Sgd78059 		ddi_prop_remove_all(dip);
5944992Sgd78059 
5954992Sgd78059 		/* free up any left over buffers or DMA resources */
5964992Sgd78059 		afe_freerxring(afep);
5974992Sgd78059 		afe_freetxring(afep);
5984992Sgd78059 
5994992Sgd78059 		ddi_regs_map_free(&afep->afe_regshandle);
6004992Sgd78059 		mutex_destroy(&afep->afe_intrlock);
6014992Sgd78059 		mutex_destroy(&afep->afe_xmtlock);
6024992Sgd78059 
6034992Sgd78059 		kmem_free(afep, sizeof (afe_t));
6044992Sgd78059 		return (DDI_SUCCESS);
6054992Sgd78059 
6064992Sgd78059 	case DDI_SUSPEND:
6079860Sgdamore@opensolaris.org 		/* stop MII monitoring */
6089860Sgdamore@opensolaris.org 		mii_suspend(afep->afe_mii);
6099860Sgdamore@opensolaris.org 
6104992Sgd78059 		/* quiesce the hardware */
6114992Sgd78059 		mutex_enter(&afep->afe_intrlock);
6124992Sgd78059 		mutex_enter(&afep->afe_xmtlock);
6134992Sgd78059 		afep->afe_flags |= AFE_SUSPENDED;
6144992Sgd78059 		afe_stopall(afep);
6154992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
6164992Sgd78059 		mutex_exit(&afep->afe_intrlock);
6174992Sgd78059 		return (DDI_SUCCESS);
6184992Sgd78059 	default:
6194992Sgd78059 		return (DDI_FAILURE);
6204992Sgd78059 	}
6214992Sgd78059 }
6224992Sgd78059 
6234992Sgd78059 int
afe_resume(dev_info_t * dip)6244992Sgd78059 afe_resume(dev_info_t *dip)
6254992Sgd78059 {
6264992Sgd78059 	afe_t	*afep;
6274992Sgd78059 
6284992Sgd78059 	if ((afep = ddi_get_driver_private(dip)) == NULL) {
6294992Sgd78059 		return (DDI_FAILURE);
6304992Sgd78059 	}
6314992Sgd78059 
6324992Sgd78059 	mutex_enter(&afep->afe_intrlock);
6334992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
6344992Sgd78059 
6354992Sgd78059 	afep->afe_flags &= ~AFE_SUSPENDED;
6364992Sgd78059 
6374992Sgd78059 	/* re-initialize chip */
6384992Sgd78059 	if (!afe_initialize(afep)) {
6394992Sgd78059 		afe_error(afep->afe_dip, "unable to resume chip!");
6404992Sgd78059 		afep->afe_flags |= AFE_SUSPENDED;
6414992Sgd78059 		mutex_exit(&afep->afe_intrlock);
6424992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
6434992Sgd78059 		return (DDI_SUCCESS);
6444992Sgd78059 	}
6454992Sgd78059 
6464992Sgd78059 	/* start the chip */
6474992Sgd78059 	if (afep->afe_flags & AFE_RUNNING) {
6484992Sgd78059 		afe_startall(afep);
6494992Sgd78059 	}
6504992Sgd78059 
6514992Sgd78059 	/* drop locks */
6524992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
6534992Sgd78059 	mutex_exit(&afep->afe_intrlock);
6544992Sgd78059 
6559860Sgdamore@opensolaris.org 	mii_resume(afep->afe_mii);
6569860Sgdamore@opensolaris.org 
6574992Sgd78059 	return (DDI_SUCCESS);
6584992Sgd78059 }
6594992Sgd78059 
6609315Sgdamore@opensolaris.org int
afe_quiesce(dev_info_t * dip)6619315Sgdamore@opensolaris.org afe_quiesce(dev_info_t *dip)
6629315Sgdamore@opensolaris.org {
6639315Sgdamore@opensolaris.org 	afe_t	*afep;
6649315Sgdamore@opensolaris.org 
6659315Sgdamore@opensolaris.org 	if ((afep = ddi_get_driver_private(dip)) == NULL) {
6669315Sgdamore@opensolaris.org 		return (DDI_FAILURE);
6679315Sgdamore@opensolaris.org 	}
6689315Sgdamore@opensolaris.org 
6699315Sgdamore@opensolaris.org 	SETBIT(afep, CSR_PAR, PAR_RESET);
6709315Sgdamore@opensolaris.org 	/*
6719315Sgdamore@opensolaris.org 	 * At 66 MHz it is 16 nsec per access or more (always more)
6729315Sgdamore@opensolaris.org 	 * So we need 3,333 times to retry for 50 usec.  We just
6739315Sgdamore@opensolaris.org 	 * round up to 5000 times.  Unless the hardware is horked,
6749315Sgdamore@opensolaris.org 	 * it will always terminate *well* before that anyway.
6759315Sgdamore@opensolaris.org 	 */
6769315Sgdamore@opensolaris.org 	for (int i = 0; i < 5000; i++) {
6779315Sgdamore@opensolaris.org 		if ((GETCSR(afep, CSR_PAR) & PAR_RESET) == 0) {
6789315Sgdamore@opensolaris.org 			return (DDI_SUCCESS);
6799315Sgdamore@opensolaris.org 		}
6809315Sgdamore@opensolaris.org 	}
6819315Sgdamore@opensolaris.org 
6829315Sgdamore@opensolaris.org 	/* hardware didn't quiesce - force a full reboot (PCI reset) */
6839315Sgdamore@opensolaris.org 	return (DDI_FAILURE);
6849315Sgdamore@opensolaris.org }
6859315Sgdamore@opensolaris.org 
6864992Sgd78059 void
afe_setrxfilt(afe_t * afep)6874992Sgd78059 afe_setrxfilt(afe_t *afep)
6884992Sgd78059 {
6894992Sgd78059 	unsigned rxen, pa0, pa1;
6904992Sgd78059 
6914992Sgd78059 	if (afep->afe_flags & AFE_SUSPENDED) {
6924992Sgd78059 		/* don't touch a suspended interface */
6934992Sgd78059 		return;
6944992Sgd78059 	}
6954992Sgd78059 
6964992Sgd78059 	rxen = GETCSR(afep, CSR_NAR) & NAR_RX_ENABLE;
6974992Sgd78059 
6984992Sgd78059 	/* stop receiver */
6994992Sgd78059 	if (rxen) {
7004992Sgd78059 		afe_stopmac(afep);
7014992Sgd78059 	}
7024992Sgd78059 
7034992Sgd78059 	/* program promiscuous mode */
7044992Sgd78059 	if (afep->afe_promisc)
7054992Sgd78059 		SETBIT(afep, CSR_NAR, NAR_RX_PROMISC);
7064992Sgd78059 	else
7074992Sgd78059 		CLRBIT(afep, CSR_NAR, NAR_RX_PROMISC);
7084992Sgd78059 
7094992Sgd78059 	/* program mac address */
7104992Sgd78059 	pa0 = (afep->afe_curraddr[3] << 24) | (afep->afe_curraddr[2] << 16) |
7114992Sgd78059 	    (afep->afe_curraddr[1] << 8) | afep->afe_curraddr[0];
7124992Sgd78059 	pa1 = (afep->afe_curraddr[5] << 8) | afep->afe_curraddr[4];
7134992Sgd78059 
7144992Sgd78059 	PUTCSR(afep, CSR_PAR0, pa0);
7154992Sgd78059 	PUTCSR(afep, CSR_PAR1, pa1);
7164992Sgd78059 	if (rxen) {
7174992Sgd78059 		SETBIT(afep, CSR_NAR, rxen);
7184992Sgd78059 	}
7194992Sgd78059 
7204992Sgd78059 	/* program multicast filter */
7214992Sgd78059 	if (AFE_MODEL(afep) == MODEL_COMET) {
7224992Sgd78059 		if (afep->afe_mctab[0] || afep->afe_mctab[1]) {
7234992Sgd78059 			SETBIT(afep, CSR_NAR, NAR_RX_MULTI);
7244992Sgd78059 		} else {
7254992Sgd78059 			CLRBIT(afep, CSR_NAR, NAR_RX_MULTI);
7264992Sgd78059 		}
7274992Sgd78059 	} else {
7284992Sgd78059 		CLRBIT(afep, CSR_NAR, NAR_RX_MULTI);
7294992Sgd78059 		PUTCSR(afep, CSR_MAR0, afep->afe_mctab[0]);
7304992Sgd78059 		PUTCSR(afep, CSR_MAR1, afep->afe_mctab[1]);
7314992Sgd78059 	}
7324992Sgd78059 
7334992Sgd78059 	/* restart receiver */
7344992Sgd78059 	if (rxen) {
7354992Sgd78059 		afe_startmac(afep);
7364992Sgd78059 	}
7374992Sgd78059 }
7384992Sgd78059 
7394992Sgd78059 int
afe_watchdog(afe_t * afep)7409860Sgdamore@opensolaris.org afe_watchdog(afe_t *afep)
7419860Sgdamore@opensolaris.org {
7429860Sgdamore@opensolaris.org 	if ((afep->afe_txstall_time != 0) &&
7439860Sgdamore@opensolaris.org 	    (gethrtime() > afep->afe_txstall_time) &&
7449860Sgdamore@opensolaris.org 	    (afep->afe_txavail != AFE_TXRING)) {
7459860Sgdamore@opensolaris.org 		afep->afe_txstall_time = 0;
7469860Sgdamore@opensolaris.org 		afe_error(afep->afe_dip, "TX stall detected!");
7479860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
7489860Sgdamore@opensolaris.org 	} else {
7499860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
7509860Sgdamore@opensolaris.org 	}
7519860Sgdamore@opensolaris.org }
7529860Sgdamore@opensolaris.org 
7539860Sgdamore@opensolaris.org int
afe_m_multicst(void * arg,boolean_t add,const uint8_t * macaddr)7544992Sgd78059 afe_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
7554992Sgd78059 {
7564992Sgd78059 	afe_t		*afep = arg;
7574992Sgd78059 	int		index;
7584992Sgd78059 	uint32_t	crc;
7594992Sgd78059 	uint32_t	bit;
7604992Sgd78059 	uint32_t	newval, oldval;
7614992Sgd78059 
7624992Sgd78059 	CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
7634992Sgd78059 	crc %= AFE_MCHASH;
7644992Sgd78059 
7654992Sgd78059 	/* bit within a 32-bit word */
7664992Sgd78059 	index = crc / 32;
7674992Sgd78059 	bit = (1 << (crc % 32));
7684992Sgd78059 
7694992Sgd78059 	mutex_enter(&afep->afe_intrlock);
7704992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
7714992Sgd78059 	newval = oldval = afep->afe_mctab[index];
7724992Sgd78059 
7734992Sgd78059 	if (add) {
7744992Sgd78059 		afep->afe_mccount[crc]++;
7754992Sgd78059 		if (afep->afe_mccount[crc] == 1)
7764992Sgd78059 			newval |= bit;
7774992Sgd78059 	} else {
7784992Sgd78059 		afep->afe_mccount[crc]--;
7794992Sgd78059 		if (afep->afe_mccount[crc] == 0)
7804992Sgd78059 			newval &= ~bit;
7814992Sgd78059 	}
7824992Sgd78059 	if (newval != oldval) {
7834992Sgd78059 		afep->afe_mctab[index] = newval;
7844992Sgd78059 		afe_setrxfilt(afep);
7854992Sgd78059 	}
7864992Sgd78059 
7874992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
7884992Sgd78059 	mutex_exit(&afep->afe_intrlock);
7894992Sgd78059 
7904992Sgd78059 	return (0);
7914992Sgd78059 }
7924992Sgd78059 
7934992Sgd78059 int
afe_m_promisc(void * arg,boolean_t on)7944992Sgd78059 afe_m_promisc(void *arg, boolean_t on)
7954992Sgd78059 {
7964992Sgd78059 	afe_t		*afep = arg;
7974992Sgd78059 
7984992Sgd78059 	/* exclusive access to the card while we reprogram it */
7994992Sgd78059 	mutex_enter(&afep->afe_intrlock);
8004992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
8014992Sgd78059 	/* save current promiscuous mode state for replay in resume */
8024992Sgd78059 	afep->afe_promisc = on;
8034992Sgd78059 
8044992Sgd78059 	afe_setrxfilt(afep);
8054992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
8064992Sgd78059 	mutex_exit(&afep->afe_intrlock);
8074992Sgd78059 
8084992Sgd78059 	return (0);
8094992Sgd78059 }
8104992Sgd78059 
8114992Sgd78059 int
afe_m_unicst(void * arg,const uint8_t * macaddr)8124992Sgd78059 afe_m_unicst(void *arg, const uint8_t *macaddr)
8134992Sgd78059 {
8144992Sgd78059 	afe_t		*afep = arg;
8154992Sgd78059 
8164992Sgd78059 	/* exclusive access to the card while we reprogram it */
8174992Sgd78059 	mutex_enter(&afep->afe_intrlock);
8184992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
8194992Sgd78059 
8204992Sgd78059 	bcopy(macaddr, afep->afe_curraddr, ETHERADDRL);
8214992Sgd78059 	afe_setrxfilt(afep);
8224992Sgd78059 
8234992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
8244992Sgd78059 	mutex_exit(&afep->afe_intrlock);
8254992Sgd78059 
8264992Sgd78059 	return (0);
8274992Sgd78059 }
8284992Sgd78059 
8294992Sgd78059 mblk_t *
afe_m_tx(void * arg,mblk_t * mp)8304992Sgd78059 afe_m_tx(void *arg, mblk_t *mp)
8314992Sgd78059 {
8324992Sgd78059 	afe_t	*afep = arg;
8334992Sgd78059 	mblk_t	*nmp;
8344992Sgd78059 
8354992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
8364992Sgd78059 
8374992Sgd78059 	if (afep->afe_flags & AFE_SUSPENDED) {
8384992Sgd78059 		while ((nmp = mp) != NULL) {
8394992Sgd78059 			afep->afe_carrier_errors++;
8404992Sgd78059 			mp = mp->b_next;
8414992Sgd78059 			freemsg(nmp);
8424992Sgd78059 		}
8434992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
8444992Sgd78059 		return (NULL);
8454992Sgd78059 	}
8464992Sgd78059 
8474992Sgd78059 	while (mp != NULL) {
8484992Sgd78059 		nmp = mp->b_next;
8494992Sgd78059 		mp->b_next = NULL;
8504992Sgd78059 
8514992Sgd78059 		if (!afe_send(afep, mp)) {
8524992Sgd78059 			mp->b_next = nmp;
8534992Sgd78059 			break;
8544992Sgd78059 		}
8554992Sgd78059 		mp = nmp;
8564992Sgd78059 	}
8574992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
8584992Sgd78059 
8594992Sgd78059 	return (mp);
8604992Sgd78059 }
8614992Sgd78059 
8629860Sgdamore@opensolaris.org void
afe_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)8639860Sgdamore@opensolaris.org afe_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
8649860Sgdamore@opensolaris.org {
8659860Sgdamore@opensolaris.org 	afe_t	*afep = arg;
8669860Sgdamore@opensolaris.org 
8679860Sgdamore@opensolaris.org 	if (mii_m_loop_ioctl(afep->afe_mii, wq, mp))
8689860Sgdamore@opensolaris.org 		return;
8699860Sgdamore@opensolaris.org 
8709860Sgdamore@opensolaris.org 	miocnak(wq, mp, 0, EINVAL);
8719860Sgdamore@opensolaris.org }
8729860Sgdamore@opensolaris.org 
8734992Sgd78059 /*
8744992Sgd78059  * Hardware management.
8754992Sgd78059  */
8764992Sgd78059 static boolean_t
afe_initialize(afe_t * afep)8774992Sgd78059 afe_initialize(afe_t *afep)
8784992Sgd78059 {
8794992Sgd78059 	int		i;
8804992Sgd78059 	unsigned	val;
8814992Sgd78059 	uint32_t	par, nar;
8824992Sgd78059 
8834992Sgd78059 	ASSERT(mutex_owned(&afep->afe_intrlock));
8844992Sgd78059 	ASSERT(mutex_owned(&afep->afe_xmtlock));
8854992Sgd78059 
8864992Sgd78059 	SETBIT(afep, CSR_PAR, PAR_RESET);
8874992Sgd78059 	for (i = 1; i < 10; i++) {
8884992Sgd78059 		drv_usecwait(5);
8894992Sgd78059 		val = GETCSR(afep, CSR_PAR);
8904992Sgd78059 		if (!(val & PAR_RESET)) {
8914992Sgd78059 			break;
8924992Sgd78059 		}
8934992Sgd78059 	}
8944992Sgd78059 	if (i == 10) {
8954992Sgd78059 		afe_error(afep->afe_dip, "timed out waiting for reset!");
8964992Sgd78059 		return (B_FALSE);
8974992Sgd78059 	}
8984992Sgd78059 
8994992Sgd78059 	/*
9004992Sgd78059 	 * Updated Centaur data sheets show that the Comet and Centaur are
9014992Sgd78059 	 * alike here (contrary to earlier versions of the data sheet).
9024992Sgd78059 	 */
9034992Sgd78059 	/* XXX:? chip problems */
9044992Sgd78059 	/* par = PAR_MRLE | PAR_MRME | PAR_MWIE; */
9054992Sgd78059 	par = 0;
9064992Sgd78059 	switch (afep->afe_cachesize) {
9074992Sgd78059 	case 8:
9084992Sgd78059 		par |= PAR_CALIGN_8 | PAR_BURST_8;
9094992Sgd78059 		break;
9104992Sgd78059 	case 16:
9114992Sgd78059 		par |= PAR_CALIGN_16 | PAR_BURST_16;
9124992Sgd78059 		break;
9134992Sgd78059 	case 32:
9144992Sgd78059 		par |= PAR_CALIGN_32 | PAR_BURST_32;
9154992Sgd78059 		break;
9164992Sgd78059 	default:
9174992Sgd78059 		par |= PAR_BURST_32;
9184992Sgd78059 		par &= ~(PAR_MWIE | PAR_MRLE | PAR_MRME);
9194992Sgd78059 		break;
9204992Sgd78059 
9214992Sgd78059 	}
9224992Sgd78059 
9234992Sgd78059 	PUTCSR(afep, CSR_PAR, par);
9244992Sgd78059 
9254992Sgd78059 	/* enable transmit underrun auto-recovery */
9264992Sgd78059 	SETBIT(afep, CSR_CR, CR_TXURAUTOR);
9274992Sgd78059 
9284992Sgd78059 	afe_resetrings(afep);
9294992Sgd78059 
9304992Sgd78059 	/* clear the lost packet counter (cleared on read) */
9314992Sgd78059 	(void) GETCSR(afep, CSR_LPC);
9324992Sgd78059 
9334992Sgd78059 	nar = GETCSR(afep, CSR_NAR);
9344992Sgd78059 	nar &= ~NAR_TR;		/* clear tx threshold */
9354992Sgd78059 	nar |= NAR_SF;		/* store-and-forward */
9364992Sgd78059 	nar |= NAR_HBD;		/* disable SQE test */
9374992Sgd78059 	PUTCSR(afep, CSR_NAR, nar);
9384992Sgd78059 
9394992Sgd78059 	afe_setrxfilt(afep);
9404992Sgd78059 
9414992Sgd78059 	return (B_TRUE);
9424992Sgd78059 }
9434992Sgd78059 
9444992Sgd78059 /*
9454992Sgd78059  * Serial EEPROM access - inspired by the FreeBSD implementation.
9464992Sgd78059  */
9474992Sgd78059 
9484992Sgd78059 uint8_t
afe_sromwidth(afe_t * afep)9494992Sgd78059 afe_sromwidth(afe_t *afep)
9504992Sgd78059 {
9514992Sgd78059 	int		i;
9524992Sgd78059 	uint32_t	eeread;
9534992Sgd78059 	uint8_t		addrlen = 8;
9544992Sgd78059 
9554992Sgd78059 	eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP;
9564992Sgd78059 
9574992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread & ~SPR_SROM_CHIP);
9584992Sgd78059 	drv_usecwait(1);
9594992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread);
9604992Sgd78059 
9614992Sgd78059 	/* command bits first */
9624992Sgd78059 	for (i = 4; i != 0; i >>= 1) {
9634992Sgd78059 		unsigned val = (SROM_READCMD & i) ? SPR_SROM_DIN : 0;
9644992Sgd78059 
9654992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | val);
9664992Sgd78059 		drv_usecwait(1);
9674992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | val | SPR_SROM_CLOCK);
9684992Sgd78059 		drv_usecwait(1);
9694992Sgd78059 	}
9704992Sgd78059 
9714992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread);
9724992Sgd78059 
9734992Sgd78059 	for (addrlen = 1; addrlen <= 12; addrlen++) {
9744992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | SPR_SROM_CLOCK);
9754992Sgd78059 		drv_usecwait(1);
9764992Sgd78059 		if (!(GETCSR(afep, CSR_SPR) & SPR_SROM_DOUT)) {
9774992Sgd78059 			PUTCSR(afep, CSR_SPR, eeread);
9784992Sgd78059 			drv_usecwait(1);
9794992Sgd78059 			break;
9804992Sgd78059 		}
9814992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread);
9824992Sgd78059 		drv_usecwait(1);
9834992Sgd78059 	}
9844992Sgd78059 
9854992Sgd78059 	/* turn off accesses to the EEPROM */
9864992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread &~ SPR_SROM_CHIP);
9874992Sgd78059 
9884992Sgd78059 	return ((addrlen < 4 || addrlen > 12) ? 6 : addrlen);
9894992Sgd78059 }
9904992Sgd78059 
9914992Sgd78059 /*
9924992Sgd78059  * The words in EEPROM are stored in little endian order.  We
9934992Sgd78059  * shift bits out in big endian order, though.  This requires
9944992Sgd78059  * a byte swap on some platforms.
9954992Sgd78059  */
9964992Sgd78059 uint16_t
afe_readsromword(afe_t * afep,unsigned romaddr)9974992Sgd78059 afe_readsromword(afe_t *afep, unsigned romaddr)
9984992Sgd78059 {
9994992Sgd78059 	int		i;
10004992Sgd78059 	uint16_t	word = 0;
10014992Sgd78059 	uint16_t	retval;
10024992Sgd78059 	int		eeread;
10034992Sgd78059 	uint8_t		addrlen;
10044992Sgd78059 	int		readcmd;
10054992Sgd78059 	uchar_t		*ptr;
10064992Sgd78059 
10074992Sgd78059 	eeread = SPR_SROM_READ | SPR_SROM_SEL | SPR_SROM_CHIP;
10084992Sgd78059 	addrlen = afep->afe_sromwidth;
10094992Sgd78059 	readcmd = (SROM_READCMD << addrlen) | romaddr;
10104992Sgd78059 
10114992Sgd78059 	if (romaddr >= (1 << addrlen)) {
10124992Sgd78059 		/* too big to fit! */
10134992Sgd78059 		return (0);
10144992Sgd78059 	}
10154992Sgd78059 
10164992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread & ~SPR_SROM_CHIP);
10174992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread);
10184992Sgd78059 
10194992Sgd78059 	/* command and address bits */
10204992Sgd78059 	for (i = 4 + addrlen; i >= 0; i--) {
10214992Sgd78059 		short val = (readcmd & (1 << i)) ? SPR_SROM_DIN : 0;
10224992Sgd78059 
10234992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | val);
10244992Sgd78059 		drv_usecwait(1);
10254992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | val | SPR_SROM_CLOCK);
10264992Sgd78059 		drv_usecwait(1);
10274992Sgd78059 	}
10284992Sgd78059 
10294992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread);
10304992Sgd78059 
10314992Sgd78059 	for (i = 0; i < 16; i++) {
10324992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread | SPR_SROM_CLOCK);
10334992Sgd78059 		drv_usecwait(1);
10344992Sgd78059 		word <<= 1;
10354992Sgd78059 		if (GETCSR(afep, CSR_SPR) & SPR_SROM_DOUT) {
10364992Sgd78059 			word |= 1;
10374992Sgd78059 		}
10384992Sgd78059 		PUTCSR(afep, CSR_SPR, eeread);
10394992Sgd78059 		drv_usecwait(1);
10404992Sgd78059 	}
10414992Sgd78059 
10424992Sgd78059 	/* turn off accesses to the EEPROM */
10434992Sgd78059 	PUTCSR(afep, CSR_SPR, eeread &~ SPR_SROM_CHIP);
10444992Sgd78059 
10454992Sgd78059 	/*
10464992Sgd78059 	 * Fix up the endianness thing.  Note that the values
10474992Sgd78059 	 * are stored in little endian format on the SROM.
10484992Sgd78059 	 */
10494992Sgd78059 	ptr = (uchar_t *)&word;
10504992Sgd78059 	retval = (ptr[1] << 8) | ptr[0];
10514992Sgd78059 	return (retval);
10524992Sgd78059 }
10534992Sgd78059 
10544992Sgd78059 void
afe_readsrom(afe_t * afep,unsigned romaddr,unsigned len,char * dest)10554992Sgd78059 afe_readsrom(afe_t *afep, unsigned romaddr, unsigned len, char *dest)
10564992Sgd78059 {
10574992Sgd78059 	int	i;
10584992Sgd78059 	uint16_t	word;
10594992Sgd78059 	uint16_t	*ptr = (uint16_t *)((void *)dest);
10604992Sgd78059 	for (i = 0; i < len; i++) {
10614992Sgd78059 		word = afe_readsromword(afep, romaddr + i);
10624992Sgd78059 		*ptr = word;
10634992Sgd78059 		ptr++;
10644992Sgd78059 	}
10654992Sgd78059 }
10664992Sgd78059 
10674992Sgd78059 void
afe_getfactaddr(afe_t * afep,uchar_t * eaddr)10684992Sgd78059 afe_getfactaddr(afe_t *afep, uchar_t *eaddr)
10694992Sgd78059 {
10704992Sgd78059 	afe_readsrom(afep, SROM_ENADDR, ETHERADDRL / 2, (char *)eaddr);
10719860Sgdamore@opensolaris.org }
10724992Sgd78059 
10739860Sgdamore@opensolaris.org 
10744992Sgd78059 
10754992Sgd78059 /*
10764992Sgd78059  * MII management.
10774992Sgd78059  */
10784992Sgd78059 void
afe_mii_reset(void * arg)10799860Sgdamore@opensolaris.org afe_mii_reset(void *arg)
10804992Sgd78059 {
10819860Sgdamore@opensolaris.org 	afe_t		*afep = arg;
10824992Sgd78059 	int		fiber;
10839860Sgdamore@opensolaris.org 	uint16_t	mcr;
10849860Sgdamore@opensolaris.org 	uint16_t	pilr;
10859860Sgdamore@opensolaris.org 	uint8_t		phyaddr;
10864992Sgd78059 
10874992Sgd78059 	/*
10889860Sgdamore@opensolaris.org 	 * Its entirely possible that this belongs as a PHY specific
10899860Sgdamore@opensolaris.org 	 * override.
10904992Sgd78059 	 */
10919860Sgdamore@opensolaris.org 	if ((mii_get_id(afep->afe_mii) & 0xfffffff0) != 0x225410) {
10929860Sgdamore@opensolaris.org 		/* if its not an AN983B, we don't care */
10934992Sgd78059 		return;
10944992Sgd78059 	}
10954992Sgd78059 
10969860Sgdamore@opensolaris.org 	phyaddr = mii_get_addr(afep->afe_mii);
10979860Sgdamore@opensolaris.org 
10989860Sgdamore@opensolaris.org 	fiber = 0;
10999860Sgdamore@opensolaris.org 
11009860Sgdamore@opensolaris.org 	switch (afep->afe_forcefiber) {
11019860Sgdamore@opensolaris.org 	case 0:
11029860Sgdamore@opensolaris.org 		/* UTP Port */
11039860Sgdamore@opensolaris.org 		fiber = 0;
11049860Sgdamore@opensolaris.org 		break;
11059860Sgdamore@opensolaris.org 	case 1:
11069860Sgdamore@opensolaris.org 		/* Fiber Port */
11079860Sgdamore@opensolaris.org 		fiber = 1;
11089860Sgdamore@opensolaris.org 		break;
11094992Sgd78059 	}
11104992Sgd78059 
11119860Sgdamore@opensolaris.org 	mcr = afe_mii_read(afep, phyaddr, PHY_MCR);
11129860Sgdamore@opensolaris.org 	switch (fiber) {
11139860Sgdamore@opensolaris.org 	case 0:
11149860Sgdamore@opensolaris.org 		mcr &= ~MCR_FIBER;
11159860Sgdamore@opensolaris.org 		break;
11164992Sgd78059 
11179860Sgdamore@opensolaris.org 	case 1:
11189860Sgdamore@opensolaris.org 		mcr |= MCR_FIBER;
11199860Sgdamore@opensolaris.org 		break;
11204992Sgd78059 	}
11219860Sgdamore@opensolaris.org 	afe_mii_write(afep, phyaddr, PHY_MCR, mcr);
11229860Sgdamore@opensolaris.org 	drv_usecwait(500);
11234992Sgd78059 
11244992Sgd78059 	/*
11259860Sgdamore@opensolaris.org 	 * work around for errata 983B_0416 -- duplex light flashes
11269860Sgdamore@opensolaris.org 	 * in 10 HDX.  we just disable SQE testing on the device.
11274992Sgd78059 	 */
11289860Sgdamore@opensolaris.org 	pilr = afe_mii_read(afep, phyaddr, PHY_PILR);
11299860Sgdamore@opensolaris.org 	pilr |= PILR_NOSQE;
11309860Sgdamore@opensolaris.org 	afe_mii_write(afep, phyaddr, PHY_PILR, pilr);
11314992Sgd78059 }
11324992Sgd78059 
11334992Sgd78059 void
afe_mii_notify(void * arg,link_state_t link)11349860Sgdamore@opensolaris.org afe_mii_notify(void *arg, link_state_t link)
11354992Sgd78059 {
11369860Sgdamore@opensolaris.org 	afe_t	*afep = arg;
11374992Sgd78059 
11389860Sgdamore@opensolaris.org 	if (AFE_MODEL(afep) == MODEL_CENTAUR) {
11399860Sgdamore@opensolaris.org 		if (mii_get_flowctrl(afep->afe_mii) == LINK_FLOWCTRL_BI) {
11409860Sgdamore@opensolaris.org 			SETBIT(afep, CSR_CR, CR_PAUSE);
11419860Sgdamore@opensolaris.org 		} else {
11429860Sgdamore@opensolaris.org 			CLRBIT(afep, CSR_CR, CR_PAUSE);
11434992Sgd78059 		}
11444992Sgd78059 	}
11459860Sgdamore@opensolaris.org 	mac_link_update(afep->afe_mh, link);
11464992Sgd78059 }
11474992Sgd78059 
11484992Sgd78059 void
afe_miitristate(afe_t * afep)11494992Sgd78059 afe_miitristate(afe_t *afep)
11504992Sgd78059 {
11516684Sgd78059 	uint32_t val = SPR_SROM_WRITE | SPR_MII_CTRL;
11526684Sgd78059 
11534992Sgd78059 	PUTCSR(afep, CSR_SPR, val);
11544992Sgd78059 	drv_usecwait(1);
11554992Sgd78059 	PUTCSR(afep, CSR_SPR, val | SPR_MII_CLOCK);
11564992Sgd78059 	drv_usecwait(1);
11574992Sgd78059 }
11584992Sgd78059 
11594992Sgd78059 void
afe_miiwritebit(afe_t * afep,uint8_t bit)11606684Sgd78059 afe_miiwritebit(afe_t *afep, uint8_t bit)
11614992Sgd78059 {
11626684Sgd78059 	uint32_t val = bit ? SPR_MII_DOUT : 0;
11636684Sgd78059 
11644992Sgd78059 	PUTCSR(afep, CSR_SPR, val);
11654992Sgd78059 	drv_usecwait(1);
11664992Sgd78059 	PUTCSR(afep, CSR_SPR, val | SPR_MII_CLOCK);
11674992Sgd78059 	drv_usecwait(1);
11684992Sgd78059 }
11694992Sgd78059 
11706684Sgd78059 uint8_t
afe_miireadbit(afe_t * afep)11714992Sgd78059 afe_miireadbit(afe_t *afep)
11724992Sgd78059 {
11736684Sgd78059 	uint32_t	val = SPR_MII_CTRL | SPR_SROM_READ;
11746684Sgd78059 	uint8_t		bit;
11756684Sgd78059 
11764992Sgd78059 	PUTCSR(afep, CSR_SPR, val);
11774992Sgd78059 	drv_usecwait(1);
11784992Sgd78059 	bit = (GETCSR(afep, CSR_SPR) & SPR_MII_DIN) ? 1 : 0;
11794992Sgd78059 	PUTCSR(afep, CSR_SPR, val | SPR_MII_CLOCK);
11804992Sgd78059 	drv_usecwait(1);
11814992Sgd78059 	return (bit);
11824992Sgd78059 }
11834992Sgd78059 
11846684Sgd78059 uint16_t
afe_mii_read(void * arg,uint8_t phy,uint8_t reg)11859860Sgdamore@opensolaris.org afe_mii_read(void *arg, uint8_t phy, uint8_t reg)
11864992Sgd78059 {
11879860Sgdamore@opensolaris.org 	afe_t *afep = arg;
11884992Sgd78059 	/*
11894992Sgd78059 	 * ADMtek bugs ignore address decode bits -- they only
11904992Sgd78059 	 * support PHY at 1.
11914992Sgd78059 	 */
11924992Sgd78059 	if (phy != 1) {
11934992Sgd78059 		return (0xffff);
11944992Sgd78059 	}
11954992Sgd78059 	switch (AFE_MODEL(afep)) {
11964992Sgd78059 	case MODEL_COMET:
11974992Sgd78059 		return (afe_miireadcomet(afep, phy, reg));
11984992Sgd78059 	case MODEL_CENTAUR:
11994992Sgd78059 		return (afe_miireadgeneral(afep, phy, reg));
12004992Sgd78059 	}
12014992Sgd78059 	return (0xffff);
12024992Sgd78059 }
12034992Sgd78059 
12046684Sgd78059 uint16_t
afe_miireadgeneral(afe_t * afep,uint8_t phy,uint8_t reg)12059860Sgdamore@opensolaris.org afe_miireadgeneral(afe_t *afep, uint8_t phy, uint8_t reg)
12064992Sgd78059 {
12076684Sgd78059 	uint16_t	value = 0;
12084992Sgd78059 	int		i;
12094992Sgd78059 
12104992Sgd78059 	/* send the 32 bit preamble */
12114992Sgd78059 	for (i = 0; i < 32; i++) {
12124992Sgd78059 		afe_miiwritebit(afep, 1);
12134992Sgd78059 	}
12144992Sgd78059 
12154992Sgd78059 	/* send the start code - 01b */
12164992Sgd78059 	afe_miiwritebit(afep, 0);
12174992Sgd78059 	afe_miiwritebit(afep, 1);
12184992Sgd78059 
12194992Sgd78059 	/* send the opcode for read, - 10b */
12204992Sgd78059 	afe_miiwritebit(afep, 1);
12214992Sgd78059 	afe_miiwritebit(afep, 0);
12224992Sgd78059 
12234992Sgd78059 	/* next we send the 5 bit phy address */
12244992Sgd78059 	for (i = 0x10; i > 0; i >>= 1) {
12254992Sgd78059 		afe_miiwritebit(afep, (phy & i) ? 1 : 0);
12264992Sgd78059 	}
12274992Sgd78059 
12284992Sgd78059 	/* the 5 bit register address goes next */
12294992Sgd78059 	for (i = 0x10; i > 0; i >>= 1) {
12304992Sgd78059 		afe_miiwritebit(afep, (reg & i) ? 1 : 0);
12314992Sgd78059 	}
12324992Sgd78059 
12334992Sgd78059 	/* turnaround - tristate followed by logic 0 */
12344992Sgd78059 	afe_miitristate(afep);
12354992Sgd78059 	afe_miiwritebit(afep, 0);
12364992Sgd78059 
12374992Sgd78059 	/* read the 16 bit register value */
12384992Sgd78059 	for (i = 0x8000; i > 0; i >>= 1) {
12394992Sgd78059 		value <<= 1;
12404992Sgd78059 		value |= afe_miireadbit(afep);
12414992Sgd78059 	}
12424992Sgd78059 	afe_miitristate(afep);
12434992Sgd78059 	return (value);
12444992Sgd78059 }
12454992Sgd78059 
12466684Sgd78059 uint16_t
afe_miireadcomet(afe_t * afep,uint8_t phy,uint8_t reg)12479860Sgdamore@opensolaris.org afe_miireadcomet(afe_t *afep, uint8_t phy, uint8_t reg)
12484992Sgd78059 {
12494992Sgd78059 	if (phy != 1) {
12504992Sgd78059 		return (0xffff);
12514992Sgd78059 	}
12524992Sgd78059 	switch (reg) {
12534992Sgd78059 	case MII_CONTROL:
12544992Sgd78059 		reg = CSR_BMCR;
12554992Sgd78059 		break;
12564992Sgd78059 	case MII_STATUS:
12574992Sgd78059 		reg = CSR_BMSR;
12584992Sgd78059 		break;
12594992Sgd78059 	case MII_PHYIDH:
12604992Sgd78059 		reg = CSR_PHYIDR1;
12614992Sgd78059 		break;
12624992Sgd78059 	case MII_PHYIDL:
12634992Sgd78059 		reg = CSR_PHYIDR2;
12644992Sgd78059 		break;
12654992Sgd78059 	case MII_AN_ADVERT:
12664992Sgd78059 		reg = CSR_ANAR;
12674992Sgd78059 		break;
12684992Sgd78059 	case MII_AN_LPABLE:
12694992Sgd78059 		reg = CSR_ANLPAR;
12704992Sgd78059 		break;
12714992Sgd78059 	case MII_AN_EXPANSION:
12724992Sgd78059 		reg = CSR_ANER;
12734992Sgd78059 		break;
12744992Sgd78059 	default:
12754992Sgd78059 		return (0);
12764992Sgd78059 	}
12774992Sgd78059 	return (GETCSR16(afep, reg) & 0xFFFF);
12784992Sgd78059 }
12794992Sgd78059 
12804992Sgd78059 void
afe_mii_write(void * arg,uint8_t phy,uint8_t reg,uint16_t val)12819860Sgdamore@opensolaris.org afe_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
12824992Sgd78059 {
12839860Sgdamore@opensolaris.org 	afe_t	*afep = arg;
12849860Sgdamore@opensolaris.org 
12854992Sgd78059 	/*
12864992Sgd78059 	 * ADMtek bugs ignore address decode bits -- they only
12874992Sgd78059 	 * support PHY at 1.
12884992Sgd78059 	 */
12894992Sgd78059 	if (phy != 1) {
12904992Sgd78059 		return;
12914992Sgd78059 	}
12924992Sgd78059 	switch (AFE_MODEL(afep)) {
12934992Sgd78059 	case MODEL_COMET:
12944992Sgd78059 		afe_miiwritecomet(afep, phy, reg, val);
12954992Sgd78059 		break;
12964992Sgd78059 	case MODEL_CENTAUR:
12974992Sgd78059 		afe_miiwritegeneral(afep, phy, reg, val);
12984992Sgd78059 		break;
12994992Sgd78059 	}
13004992Sgd78059 }
13014992Sgd78059 
13024992Sgd78059 void
afe_miiwritegeneral(afe_t * afep,uint8_t phy,uint8_t reg,uint16_t val)13039860Sgdamore@opensolaris.org afe_miiwritegeneral(afe_t *afep, uint8_t phy, uint8_t reg, uint16_t val)
13044992Sgd78059 {
13054992Sgd78059 	int i;
13064992Sgd78059 
13074992Sgd78059 	/* send the 32 bit preamble */
13084992Sgd78059 	for (i = 0; i < 32; i++) {
13094992Sgd78059 		afe_miiwritebit(afep, 1);
13104992Sgd78059 	}
13114992Sgd78059 
13124992Sgd78059 	/* send the start code - 01b */
13134992Sgd78059 	afe_miiwritebit(afep, 0);
13144992Sgd78059 	afe_miiwritebit(afep, 1);
13154992Sgd78059 
13164992Sgd78059 	/* send the opcode for write, - 01b */
13174992Sgd78059 	afe_miiwritebit(afep, 0);
13184992Sgd78059 	afe_miiwritebit(afep, 1);
13194992Sgd78059 
13204992Sgd78059 	/* next we send the 5 bit phy address */
13214992Sgd78059 	for (i = 0x10; i > 0; i >>= 1) {
13224992Sgd78059 		afe_miiwritebit(afep, (phy & i) ? 1 : 0);
13234992Sgd78059 	}
13244992Sgd78059 
13254992Sgd78059 	/* the 5 bit register address goes next */
13264992Sgd78059 	for (i = 0x10; i > 0; i >>= 1) {
13274992Sgd78059 		afe_miiwritebit(afep, (reg & i) ? 1 : 0);
13284992Sgd78059 	}
13294992Sgd78059 
13309860Sgdamore@opensolaris.org 	/* turnaround - 1 bit followed by logic 0 */
13319860Sgdamore@opensolaris.org 	afe_miiwritebit(afep, 1);
13324992Sgd78059 	afe_miiwritebit(afep, 0);
13334992Sgd78059 
13344992Sgd78059 	/* now write out our data (16 bits) */
13354992Sgd78059 	for (i = 0x8000; i > 0; i >>= 1) {
13364992Sgd78059 		afe_miiwritebit(afep, (val & i) ? 1 : 0);
13374992Sgd78059 	}
13384992Sgd78059 
13394992Sgd78059 	/* idle mode */
13404992Sgd78059 	afe_miitristate(afep);
13414992Sgd78059 }
13424992Sgd78059 
13434992Sgd78059 void
afe_miiwritecomet(afe_t * afep,uint8_t phy,uint8_t reg,uint16_t val)13449860Sgdamore@opensolaris.org afe_miiwritecomet(afe_t *afep, uint8_t phy, uint8_t reg, uint16_t val)
13454992Sgd78059 {
13464992Sgd78059 	if (phy != 1) {
13474992Sgd78059 		return;
13484992Sgd78059 	}
13494992Sgd78059 	switch (reg) {
13504992Sgd78059 	case MII_CONTROL:
13514992Sgd78059 		reg = CSR_BMCR;
13524992Sgd78059 		break;
13534992Sgd78059 	case MII_STATUS:
13544992Sgd78059 		reg = CSR_BMSR;
13554992Sgd78059 		break;
13564992Sgd78059 	case MII_PHYIDH:
13574992Sgd78059 		reg = CSR_PHYIDR1;
13584992Sgd78059 		break;
13594992Sgd78059 	case MII_PHYIDL:
13604992Sgd78059 		reg = CSR_PHYIDR2;
13614992Sgd78059 		break;
13624992Sgd78059 	case MII_AN_ADVERT:
13634992Sgd78059 		reg = CSR_ANAR;
13644992Sgd78059 		break;
13654992Sgd78059 	case MII_AN_LPABLE:
13664992Sgd78059 		reg = CSR_ANLPAR;
13674992Sgd78059 		break;
13684992Sgd78059 	case MII_AN_EXPANSION:
13694992Sgd78059 		reg = CSR_ANER;
13704992Sgd78059 		break;
13714992Sgd78059 	default:
13724992Sgd78059 		return;
13734992Sgd78059 	}
13744992Sgd78059 	PUTCSR16(afep, reg, val);
13754992Sgd78059 }
13764992Sgd78059 
13774992Sgd78059 int
afe_m_start(void * arg)13784992Sgd78059 afe_m_start(void *arg)
13794992Sgd78059 {
13804992Sgd78059 	afe_t	*afep = arg;
13814992Sgd78059 
13824992Sgd78059 	/* grab exclusive access to the card */
13834992Sgd78059 	mutex_enter(&afep->afe_intrlock);
13844992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
13854992Sgd78059 
13864992Sgd78059 	afe_startall(afep);
13874992Sgd78059 	afep->afe_flags |= AFE_RUNNING;
13884992Sgd78059 
13894992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
13904992Sgd78059 	mutex_exit(&afep->afe_intrlock);
13919860Sgdamore@opensolaris.org 
13929860Sgdamore@opensolaris.org 	mii_start(afep->afe_mii);
13939860Sgdamore@opensolaris.org 
13944992Sgd78059 	return (0);
13954992Sgd78059 }
13964992Sgd78059 
13974992Sgd78059 void
afe_m_stop(void * arg)13984992Sgd78059 afe_m_stop(void *arg)
13994992Sgd78059 {
14004992Sgd78059 	afe_t	*afep = arg;
14014992Sgd78059 
14029860Sgdamore@opensolaris.org 	mii_stop(afep->afe_mii);
14039860Sgdamore@opensolaris.org 
14044992Sgd78059 	/* exclusive access to the hardware! */
14054992Sgd78059 	mutex_enter(&afep->afe_intrlock);
14064992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
14074992Sgd78059 
14084992Sgd78059 	afe_stopall(afep);
14094992Sgd78059 	afep->afe_flags &= ~AFE_RUNNING;
14104992Sgd78059 
14114992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
14124992Sgd78059 	mutex_exit(&afep->afe_intrlock);
14134992Sgd78059 }
14144992Sgd78059 
14154992Sgd78059 void
afe_startmac(afe_t * afep)14164992Sgd78059 afe_startmac(afe_t *afep)
14174992Sgd78059 {
14184992Sgd78059 	/* verify exclusive access to the card */
14194992Sgd78059 	ASSERT(mutex_owned(&afep->afe_intrlock));
14204992Sgd78059 	ASSERT(mutex_owned(&afep->afe_xmtlock));
14214992Sgd78059 
14224992Sgd78059 	/* start the card */
14234992Sgd78059 	SETBIT(afep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE);
14244992Sgd78059 
14254992Sgd78059 	if (afep->afe_txavail != AFE_TXRING)
14264992Sgd78059 		PUTCSR(afep, CSR_TDR, 0);
14274992Sgd78059 
14284992Sgd78059 	/* tell the mac that we are ready to go! */
14294992Sgd78059 	if (afep->afe_flags & AFE_RUNNING)
14304992Sgd78059 		mac_tx_update(afep->afe_mh);
14319860Sgdamore@opensolaris.org 
14329860Sgdamore@opensolaris.org 	/* start watchdog timer */
14339860Sgdamore@opensolaris.org 	PUTCSR(afep, CSR_TIMER, TIMER_LOOP |
14349860Sgdamore@opensolaris.org 	    (AFE_WDOGTIMER * 1000 / TIMER_USEC));
14354992Sgd78059 }
14364992Sgd78059 
14374992Sgd78059 void
afe_stopmac(afe_t * afep)14384992Sgd78059 afe_stopmac(afe_t *afep)
14394992Sgd78059 {
14404992Sgd78059 	int		i;
14414992Sgd78059 
14424992Sgd78059 	/* exclusive access to the hardware! */
14434992Sgd78059 	ASSERT(mutex_owned(&afep->afe_intrlock));
14444992Sgd78059 	ASSERT(mutex_owned(&afep->afe_xmtlock));
14454992Sgd78059 
14464992Sgd78059 	CLRBIT(afep, CSR_NAR, NAR_TX_ENABLE | NAR_RX_ENABLE);
14474992Sgd78059 
14484992Sgd78059 	/*
14494992Sgd78059 	 * A 1518 byte frame at 10Mbps takes about 1.2 msec to drain.
14504992Sgd78059 	 * We just add up to the nearest msec (2), which should be
14514992Sgd78059 	 * plenty to complete.
14524992Sgd78059 	 *
14534992Sgd78059 	 * Note that some chips never seem to indicate the transition to
14544992Sgd78059 	 * the stopped state properly.  Experience shows that we can safely
14554992Sgd78059 	 * proceed anyway, after waiting the requisite timeout.
14564992Sgd78059 	 */
14574992Sgd78059 	for (i = 2000; i != 0; i -= 10) {
14584992Sgd78059 		if ((GETCSR(afep, CSR_SR) & (SR_TX_STATE | SR_RX_STATE)) == 0)
14594992Sgd78059 			break;
14604992Sgd78059 		drv_usecwait(10);
14614992Sgd78059 	}
14624992Sgd78059 
14634992Sgd78059 	/* prevent an interrupt */
14644992Sgd78059 	PUTCSR(afep, CSR_SR2, INT_RXSTOPPED | INT_TXSTOPPED);
14659860Sgdamore@opensolaris.org 
14669860Sgdamore@opensolaris.org 	/* stop the watchdog timer */
14679860Sgdamore@opensolaris.org 	PUTCSR(afep, CSR_TIMER, 0);
14684992Sgd78059 }
14694992Sgd78059 
14704992Sgd78059 void
afe_resetrings(afe_t * afep)14714992Sgd78059 afe_resetrings(afe_t *afep)
14724992Sgd78059 {
14734992Sgd78059 	int	i;
14744992Sgd78059 
14754992Sgd78059 	/* now we need to reset the pointers... */
14764992Sgd78059 	PUTCSR(afep, CSR_RDB, 0);
14774992Sgd78059 	PUTCSR(afep, CSR_TDB, 0);
14784992Sgd78059 
14794992Sgd78059 	/* reset the descriptor ring pointers */
14804992Sgd78059 	afep->afe_rxhead = 0;
14814992Sgd78059 	afep->afe_txreclaim = 0;
14824992Sgd78059 	afep->afe_txsend = 0;
14834992Sgd78059 	afep->afe_txavail = AFE_TXRING;
14844992Sgd78059 
14854992Sgd78059 	/* set up transmit descriptor ring */
14864992Sgd78059 	for (i = 0; i < AFE_TXRING; i++) {
14874992Sgd78059 		afe_desc_t	*tmdp = &afep->afe_txdescp[i];
14884992Sgd78059 		unsigned	control = 0;
14894992Sgd78059 		if (i == (AFE_TXRING - 1)) {
14904992Sgd78059 			control |= TXCTL_ENDRING;
14914992Sgd78059 		}
14924992Sgd78059 		PUTTXDESC(afep, tmdp->desc_status, 0);
14934992Sgd78059 		PUTTXDESC(afep, tmdp->desc_control, control);
14944992Sgd78059 		PUTTXDESC(afep, tmdp->desc_buffer1, 0);
14954992Sgd78059 		PUTTXDESC(afep, tmdp->desc_buffer2, 0);
14964992Sgd78059 		SYNCTXDESC(afep, i, DDI_DMA_SYNC_FORDEV);
14974992Sgd78059 	}
14984992Sgd78059 	PUTCSR(afep, CSR_TDB, afep->afe_txdesc_paddr);
14994992Sgd78059 
15004992Sgd78059 	/* make the receive buffers available */
15014992Sgd78059 	for (i = 0; i < AFE_RXRING; i++) {
15024992Sgd78059 		afe_rxbuf_t	*rxb = afep->afe_rxbufs[i];
15034992Sgd78059 		afe_desc_t	*rmdp = &afep->afe_rxdescp[i];
15044992Sgd78059 		unsigned	control;
15054992Sgd78059 
15064992Sgd78059 		control = AFE_BUFSZ & RXCTL_BUFLEN1;
15074992Sgd78059 		if (i == (AFE_RXRING - 1)) {
15084992Sgd78059 			control |= RXCTL_ENDRING;
15094992Sgd78059 		}
15104992Sgd78059 		PUTRXDESC(afep, rmdp->desc_buffer1, rxb->rxb_paddr);
15114992Sgd78059 		PUTRXDESC(afep, rmdp->desc_buffer2, 0);
15124992Sgd78059 		PUTRXDESC(afep, rmdp->desc_control, control);
15134992Sgd78059 		PUTRXDESC(afep, rmdp->desc_status, RXSTAT_OWN);
15144992Sgd78059 		SYNCRXDESC(afep, i, DDI_DMA_SYNC_FORDEV);
15154992Sgd78059 	}
15164992Sgd78059 	PUTCSR(afep, CSR_RDB, afep->afe_rxdesc_paddr);
15174992Sgd78059 }
15184992Sgd78059 
15194992Sgd78059 void
afe_stopall(afe_t * afep)15204992Sgd78059 afe_stopall(afe_t *afep)
15214992Sgd78059 {
15224992Sgd78059 	afe_disableinterrupts(afep);
15234992Sgd78059 	afe_stopmac(afep);
15244992Sgd78059 }
15254992Sgd78059 
15264992Sgd78059 void
afe_startall(afe_t * afep)15274992Sgd78059 afe_startall(afe_t *afep)
15284992Sgd78059 {
15294992Sgd78059 	ASSERT(mutex_owned(&afep->afe_intrlock));
15304992Sgd78059 	ASSERT(mutex_owned(&afep->afe_xmtlock));
15314992Sgd78059 
15324992Sgd78059 	/* make sure interrupts are disabled to begin */
15334992Sgd78059 	afe_disableinterrupts(afep);
15344992Sgd78059 
15354992Sgd78059 	/* initialize the chip */
15364992Sgd78059 	(void) afe_initialize(afep);
15374992Sgd78059 
15384992Sgd78059 	/* now we can enable interrupts */
15394992Sgd78059 	afe_enableinterrupts(afep);
15404992Sgd78059 
15414992Sgd78059 	/* start up the mac */
15424992Sgd78059 	afe_startmac(afep);
15434992Sgd78059 }
15444992Sgd78059 
15454992Sgd78059 void
afe_resetall(afe_t * afep)15464992Sgd78059 afe_resetall(afe_t *afep)
15474992Sgd78059 {
15484992Sgd78059 	afe_stopall(afep);
15494992Sgd78059 	afe_startall(afep);
15504992Sgd78059 }
15514992Sgd78059 
15524992Sgd78059 afe_txbuf_t *
afe_alloctxbuf(afe_t * afep)15534992Sgd78059 afe_alloctxbuf(afe_t *afep)
15544992Sgd78059 {
15554992Sgd78059 	ddi_dma_cookie_t	dmac;
15564992Sgd78059 	unsigned		ncookies;
15574992Sgd78059 	afe_txbuf_t		*txb;
15584992Sgd78059 	size_t			len;
15594992Sgd78059 
15604992Sgd78059 	txb = kmem_zalloc(sizeof (*txb), KM_SLEEP);
15614992Sgd78059 
15624992Sgd78059 	if (ddi_dma_alloc_handle(afep->afe_dip, &afe_dma_txattr,
15634992Sgd78059 	    DDI_DMA_SLEEP, NULL, &txb->txb_dmah) != DDI_SUCCESS) {
15644992Sgd78059 		return (NULL);
15654992Sgd78059 	}
15664992Sgd78059 
15674992Sgd78059 	if (ddi_dma_mem_alloc(txb->txb_dmah, AFE_BUFSZ, &afe_bufattr,
15684992Sgd78059 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &txb->txb_buf, &len,
15694992Sgd78059 	    &txb->txb_acch) != DDI_SUCCESS) {
15704992Sgd78059 		return (NULL);
15714992Sgd78059 	}
15724992Sgd78059 	if (ddi_dma_addr_bind_handle(txb->txb_dmah, NULL, txb->txb_buf,
15734992Sgd78059 	    len, DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
15744992Sgd78059 	    &dmac, &ncookies) != DDI_DMA_MAPPED) {
15754992Sgd78059 		return (NULL);
15764992Sgd78059 	}
15774992Sgd78059 	txb->txb_paddr = dmac.dmac_address;
15784992Sgd78059 
15794992Sgd78059 	return (txb);
15804992Sgd78059 }
15814992Sgd78059 
15824992Sgd78059 void
afe_destroytxbuf(afe_txbuf_t * txb)15834992Sgd78059 afe_destroytxbuf(afe_txbuf_t *txb)
15844992Sgd78059 {
15854992Sgd78059 	if (txb != NULL) {
15864992Sgd78059 		if (txb->txb_paddr)
15874992Sgd78059 			(void) ddi_dma_unbind_handle(txb->txb_dmah);
15884992Sgd78059 		if (txb->txb_acch)
15894992Sgd78059 			ddi_dma_mem_free(&txb->txb_acch);
15904992Sgd78059 		if (txb->txb_dmah)
15914992Sgd78059 			ddi_dma_free_handle(&txb->txb_dmah);
15924992Sgd78059 		kmem_free(txb, sizeof (*txb));
15934992Sgd78059 	}
15944992Sgd78059 }
15954992Sgd78059 
15964992Sgd78059 afe_rxbuf_t *
afe_allocrxbuf(afe_t * afep)15974992Sgd78059 afe_allocrxbuf(afe_t *afep)
15984992Sgd78059 {
15994992Sgd78059 	afe_rxbuf_t		*rxb;
16004992Sgd78059 	size_t			len;
16014992Sgd78059 	unsigned		ccnt;
16024992Sgd78059 	ddi_dma_cookie_t	dmac;
16034992Sgd78059 
16044992Sgd78059 	rxb = kmem_zalloc(sizeof (*rxb), KM_SLEEP);
16054992Sgd78059 
16064992Sgd78059 	if (ddi_dma_alloc_handle(afep->afe_dip, &afe_dma_attr,
16074992Sgd78059 	    DDI_DMA_SLEEP, NULL, &rxb->rxb_dmah) != DDI_SUCCESS) {
16084992Sgd78059 		kmem_free(rxb, sizeof (*rxb));
16094992Sgd78059 		return (NULL);
16104992Sgd78059 	}
16114992Sgd78059 	if (ddi_dma_mem_alloc(rxb->rxb_dmah, AFE_BUFSZ, &afe_bufattr,
16124992Sgd78059 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &rxb->rxb_buf, &len,
16134992Sgd78059 	    &rxb->rxb_acch) != DDI_SUCCESS) {
16144992Sgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
16154992Sgd78059 		kmem_free(rxb, sizeof (*rxb));
16164992Sgd78059 		return (NULL);
16174992Sgd78059 	}
16184992Sgd78059 	if (ddi_dma_addr_bind_handle(rxb->rxb_dmah, NULL, rxb->rxb_buf, len,
16194992Sgd78059 	    DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
16204992Sgd78059 	    &ccnt) != DDI_DMA_MAPPED) {
16214992Sgd78059 		ddi_dma_mem_free(&rxb->rxb_acch);
16224992Sgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
16234992Sgd78059 		kmem_free(rxb, sizeof (*rxb));
16244992Sgd78059 		return (NULL);
16254992Sgd78059 	}
16264992Sgd78059 	rxb->rxb_paddr = dmac.dmac_address;
16274992Sgd78059 
16284992Sgd78059 	return (rxb);
16294992Sgd78059 }
16304992Sgd78059 
16314992Sgd78059 void
afe_destroyrxbuf(afe_rxbuf_t * rxb)16324992Sgd78059 afe_destroyrxbuf(afe_rxbuf_t *rxb)
16334992Sgd78059 {
16344992Sgd78059 	if (rxb) {
16354992Sgd78059 		(void) ddi_dma_unbind_handle(rxb->rxb_dmah);
16364992Sgd78059 		ddi_dma_mem_free(&rxb->rxb_acch);
16374992Sgd78059 		ddi_dma_free_handle(&rxb->rxb_dmah);
16384992Sgd78059 		kmem_free(rxb, sizeof (*rxb));
16394992Sgd78059 	}
16404992Sgd78059 }
16414992Sgd78059 
16424992Sgd78059 /*
16434992Sgd78059  * Allocate receive resources.
16444992Sgd78059  */
16454992Sgd78059 int
afe_allocrxring(afe_t * afep)16464992Sgd78059 afe_allocrxring(afe_t *afep)
16474992Sgd78059 {
16484992Sgd78059 	int			rval;
16494992Sgd78059 	int			i;
16504992Sgd78059 	size_t			size;
16514992Sgd78059 	size_t			len;
16524992Sgd78059 	ddi_dma_cookie_t	dmac;
16534992Sgd78059 	unsigned		ncookies;
16544992Sgd78059 	caddr_t			kaddr;
16554992Sgd78059 
16564992Sgd78059 	size = AFE_RXRING * sizeof (afe_desc_t);
16574992Sgd78059 
16584992Sgd78059 	rval = ddi_dma_alloc_handle(afep->afe_dip, &afe_dma_attr,
16594992Sgd78059 	    DDI_DMA_SLEEP, NULL, &afep->afe_rxdesc_dmah);
16604992Sgd78059 	if (rval != DDI_SUCCESS) {
16614992Sgd78059 		afe_error(afep->afe_dip,
16624992Sgd78059 		    "unable to allocate DMA handle for rx descriptors");
16634992Sgd78059 		return (DDI_FAILURE);
16644992Sgd78059 	}
16654992Sgd78059 
16664992Sgd78059 	rval = ddi_dma_mem_alloc(afep->afe_rxdesc_dmah, size, &afe_devattr,
16674992Sgd78059 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
16684992Sgd78059 	    &afep->afe_rxdesc_acch);
16694992Sgd78059 	if (rval != DDI_SUCCESS) {
16704992Sgd78059 		afe_error(afep->afe_dip,
16714992Sgd78059 		    "unable to allocate DMA memory for rx descriptors");
16724992Sgd78059 		return (DDI_FAILURE);
16734992Sgd78059 	}
16744992Sgd78059 
16754992Sgd78059 	rval = ddi_dma_addr_bind_handle(afep->afe_rxdesc_dmah, NULL, kaddr,
16764992Sgd78059 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
16774992Sgd78059 	    &dmac, &ncookies);
16784992Sgd78059 	if (rval != DDI_DMA_MAPPED) {
16794992Sgd78059 		afe_error(afep->afe_dip,
16804992Sgd78059 		    "unable to bind DMA for rx descriptors");
16814992Sgd78059 		return (DDI_FAILURE);
16824992Sgd78059 	}
16834992Sgd78059 
16844992Sgd78059 	/* because of afe_dma_attr */
16854992Sgd78059 	ASSERT(ncookies == 1);
16864992Sgd78059 
16874992Sgd78059 	/* we take the 32-bit physical address out of the cookie */
16884992Sgd78059 	afep->afe_rxdesc_paddr = dmac.dmac_address;
16894992Sgd78059 	afep->afe_rxdescp = (void *)kaddr;
16904992Sgd78059 
16914992Sgd78059 	/* allocate buffer pointers (not the buffers themselves, yet) */
16924992Sgd78059 	afep->afe_rxbufs = kmem_zalloc(AFE_RXRING * sizeof (afe_rxbuf_t *),
16934992Sgd78059 	    KM_SLEEP);
16944992Sgd78059 
16954992Sgd78059 	/* now allocate rx buffers */
16964992Sgd78059 	for (i = 0; i < AFE_RXRING; i++) {
16974992Sgd78059 		afe_rxbuf_t *rxb = afe_allocrxbuf(afep);
16984992Sgd78059 		if (rxb == NULL)
16994992Sgd78059 			return (DDI_FAILURE);
17004992Sgd78059 		afep->afe_rxbufs[i] = rxb;
17014992Sgd78059 	}
17024992Sgd78059 
17034992Sgd78059 	return (DDI_SUCCESS);
17044992Sgd78059 }
17054992Sgd78059 
17064992Sgd78059 /*
17074992Sgd78059  * Allocate transmit resources.
17084992Sgd78059  */
17094992Sgd78059 int
afe_alloctxring(afe_t * afep)17104992Sgd78059 afe_alloctxring(afe_t *afep)
17114992Sgd78059 {
17124992Sgd78059 	int			rval;
17134992Sgd78059 	int			i;
17144992Sgd78059 	size_t			size;
17154992Sgd78059 	size_t			len;
17164992Sgd78059 	ddi_dma_cookie_t	dmac;
17174992Sgd78059 	unsigned		ncookies;
17184992Sgd78059 	caddr_t			kaddr;
17194992Sgd78059 
17204992Sgd78059 	size = AFE_TXRING * sizeof (afe_desc_t);
17214992Sgd78059 
17224992Sgd78059 	rval = ddi_dma_alloc_handle(afep->afe_dip, &afe_dma_attr,
17234992Sgd78059 	    DDI_DMA_SLEEP, NULL, &afep->afe_txdesc_dmah);
17244992Sgd78059 	if (rval != DDI_SUCCESS) {
17254992Sgd78059 		afe_error(afep->afe_dip,
17264992Sgd78059 		    "unable to allocate DMA handle for tx descriptors");
17274992Sgd78059 		return (DDI_FAILURE);
17284992Sgd78059 	}
17294992Sgd78059 
17304992Sgd78059 	rval = ddi_dma_mem_alloc(afep->afe_txdesc_dmah, size, &afe_devattr,
17314992Sgd78059 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
17324992Sgd78059 	    &afep->afe_txdesc_acch);
17334992Sgd78059 	if (rval != DDI_SUCCESS) {
17344992Sgd78059 		afe_error(afep->afe_dip,
17354992Sgd78059 		    "unable to allocate DMA memory for tx descriptors");
17364992Sgd78059 		return (DDI_FAILURE);
17374992Sgd78059 	}
17384992Sgd78059 
17394992Sgd78059 	rval = ddi_dma_addr_bind_handle(afep->afe_txdesc_dmah, NULL, kaddr,
17404992Sgd78059 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
17414992Sgd78059 	    &dmac, &ncookies);
17424992Sgd78059 	if (rval != DDI_DMA_MAPPED) {
17434992Sgd78059 		afe_error(afep->afe_dip,
17444992Sgd78059 		    "unable to bind DMA for tx descriptors");
17454992Sgd78059 		return (DDI_FAILURE);
17464992Sgd78059 	}
17474992Sgd78059 
17484992Sgd78059 	/* because of afe_dma_attr */
17494992Sgd78059 	ASSERT(ncookies == 1);
17504992Sgd78059 
17514992Sgd78059 	/* we take the 32-bit physical address out of the cookie */
17524992Sgd78059 	afep->afe_txdesc_paddr = dmac.dmac_address;
17534992Sgd78059 	afep->afe_txdescp = (void *)kaddr;
17544992Sgd78059 
17554992Sgd78059 	/* allocate buffer pointers (not the buffers themselves, yet) */
17564992Sgd78059 	afep->afe_txbufs = kmem_zalloc(AFE_TXRING * sizeof (afe_txbuf_t *),
17574992Sgd78059 	    KM_SLEEP);
17584992Sgd78059 
17594992Sgd78059 	/* now allocate tx buffers */
17604992Sgd78059 	for (i = 0; i < AFE_TXRING; i++) {
17614992Sgd78059 		afe_txbuf_t *txb = afe_alloctxbuf(afep);
17624992Sgd78059 		if (txb == NULL)
17634992Sgd78059 			return (DDI_FAILURE);
17644992Sgd78059 		afep->afe_txbufs[i] = txb;
17654992Sgd78059 	}
17664992Sgd78059 
17674992Sgd78059 	return (DDI_SUCCESS);
17684992Sgd78059 }
17694992Sgd78059 
17704992Sgd78059 void
afe_freerxring(afe_t * afep)17714992Sgd78059 afe_freerxring(afe_t *afep)
17724992Sgd78059 {
17734992Sgd78059 	int		i;
17744992Sgd78059 
17754992Sgd78059 	for (i = 0; i < AFE_RXRING; i++) {
17764992Sgd78059 		afe_destroyrxbuf(afep->afe_rxbufs[i]);
17774992Sgd78059 	}
17784992Sgd78059 
17794992Sgd78059 	if (afep->afe_rxbufs) {
17804992Sgd78059 		kmem_free(afep->afe_rxbufs,
17814992Sgd78059 		    AFE_RXRING * sizeof (afe_rxbuf_t *));
17824992Sgd78059 	}
17834992Sgd78059 
17844992Sgd78059 	if (afep->afe_rxdesc_paddr)
17854992Sgd78059 		(void) ddi_dma_unbind_handle(afep->afe_rxdesc_dmah);
17864992Sgd78059 	if (afep->afe_rxdesc_acch)
17874992Sgd78059 		ddi_dma_mem_free(&afep->afe_rxdesc_acch);
17884992Sgd78059 	if (afep->afe_rxdesc_dmah)
17894992Sgd78059 		ddi_dma_free_handle(&afep->afe_rxdesc_dmah);
17904992Sgd78059 }
17914992Sgd78059 
17924992Sgd78059 void
afe_freetxring(afe_t * afep)17934992Sgd78059 afe_freetxring(afe_t *afep)
17944992Sgd78059 {
17954992Sgd78059 	int			i;
17964992Sgd78059 
17974992Sgd78059 	for (i = 0; i < AFE_TXRING; i++) {
17984992Sgd78059 		afe_destroytxbuf(afep->afe_txbufs[i]);
17994992Sgd78059 	}
18004992Sgd78059 
18014992Sgd78059 	if (afep->afe_txbufs) {
18024992Sgd78059 		kmem_free(afep->afe_txbufs,
18034992Sgd78059 		    AFE_TXRING * sizeof (afe_txbuf_t *));
18044992Sgd78059 	}
18054992Sgd78059 	if (afep->afe_txdesc_paddr)
18064992Sgd78059 		(void) ddi_dma_unbind_handle(afep->afe_txdesc_dmah);
18074992Sgd78059 	if (afep->afe_txdesc_acch)
18084992Sgd78059 		ddi_dma_mem_free(&afep->afe_txdesc_acch);
18094992Sgd78059 	if (afep->afe_txdesc_dmah)
18104992Sgd78059 		ddi_dma_free_handle(&afep->afe_txdesc_dmah);
18114992Sgd78059 }
18124992Sgd78059 
18134992Sgd78059 /*
18144992Sgd78059  * Interrupt service routine.
18154992Sgd78059  */
18164992Sgd78059 unsigned
afe_intr(caddr_t arg)18174992Sgd78059 afe_intr(caddr_t arg)
18184992Sgd78059 {
18194992Sgd78059 	afe_t		*afep = (void *)arg;
18204992Sgd78059 	uint32_t	status;
18214992Sgd78059 	mblk_t		*mp = NULL;
18229860Sgdamore@opensolaris.org 	boolean_t	doreset = B_FALSE;
18234992Sgd78059 
18244992Sgd78059 	mutex_enter(&afep->afe_intrlock);
18254992Sgd78059 
18264992Sgd78059 	if (afep->afe_flags & AFE_SUSPENDED) {
18274992Sgd78059 		/* we cannot receive interrupts! */
18284992Sgd78059 		mutex_exit(&afep->afe_intrlock);
18294992Sgd78059 		return (DDI_INTR_UNCLAIMED);
18304992Sgd78059 	}
18314992Sgd78059 
18324992Sgd78059 	/* check interrupt status bits, did we interrupt? */
18334992Sgd78059 	status = GETCSR(afep, CSR_SR2) & INT_ALL;
18344992Sgd78059 
18354992Sgd78059 	if (status == 0) {
18364992Sgd78059 		KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
18374992Sgd78059 		mutex_exit(&afep->afe_intrlock);
18384992Sgd78059 		return (DDI_INTR_UNCLAIMED);
18394992Sgd78059 	}
18404992Sgd78059 	/* ack the interrupt */
18414992Sgd78059 	PUTCSR(afep, CSR_SR2, status);
18424992Sgd78059 	KIOIP->intrs[KSTAT_INTR_HARD]++;
18434992Sgd78059 
18444992Sgd78059 	if (!(afep->afe_flags & AFE_RUNNING)) {
18454992Sgd78059 		/* not running, don't touch anything */
18464992Sgd78059 		mutex_exit(&afep->afe_intrlock);
18474992Sgd78059 		return (DDI_INTR_CLAIMED);
18484992Sgd78059 	}
18494992Sgd78059 
18504992Sgd78059 	if (status & (INT_RXOK|INT_RXNOBUF)) {
18514992Sgd78059 		/* receive packets */
18524992Sgd78059 		mp = afe_receive(afep);
18534992Sgd78059 		if (status & INT_RXNOBUF)
18544992Sgd78059 			PUTCSR(afep, CSR_RDR, 0);	/* wake up chip */
18554992Sgd78059 	}
18564992Sgd78059 
18574992Sgd78059 	if (status & INT_TXOK) {
18584992Sgd78059 		/* transmit completed */
18594992Sgd78059 		mutex_enter(&afep->afe_xmtlock);
18604992Sgd78059 		afe_reclaim(afep);
18614992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
18624992Sgd78059 	}
18634992Sgd78059 
18649860Sgdamore@opensolaris.org 	if ((status & INT_TIMER) && (afe_watchdog(afep) != DDI_SUCCESS)) {
18659860Sgdamore@opensolaris.org 		doreset = B_TRUE;
18664992Sgd78059 	}
18674992Sgd78059 
18684992Sgd78059 	if (status & (INT_RXSTOPPED|INT_TXSTOPPED|
18694992Sgd78059 	    INT_RXJABBER|INT_TXJABBER|INT_TXUNDERFLOW)) {
18704992Sgd78059 
18714992Sgd78059 		if (status & (INT_RXJABBER | INT_TXJABBER)) {
18724992Sgd78059 			afep->afe_jabber++;
18734992Sgd78059 		}
18749860Sgdamore@opensolaris.org 		doreset = B_TRUE;
18754992Sgd78059 	}
18764992Sgd78059 
18774992Sgd78059 	if (status & INT_BUSERR) {
18784992Sgd78059 		switch (GETCSR(afep, CSR_SR) & SR_BERR_TYPE) {
18794992Sgd78059 		case SR_BERR_PARITY:
18804992Sgd78059 			afe_error(afep->afe_dip, "PCI parity error");
18814992Sgd78059 			break;
18824992Sgd78059 		case SR_BERR_TARGET_ABORT:
18834992Sgd78059 			afe_error(afep->afe_dip, "PCI target abort");
18844992Sgd78059 			break;
18854992Sgd78059 		case SR_BERR_MASTER_ABORT:
18864992Sgd78059 			afe_error(afep->afe_dip, "PCI master abort");
18874992Sgd78059 			break;
18884992Sgd78059 		default:
18894992Sgd78059 			afe_error(afep->afe_dip, "Unknown PCI error");
18904992Sgd78059 			break;
18914992Sgd78059 		}
18924992Sgd78059 
18934992Sgd78059 		/* reset the chip in an attempt to fix things */
18949860Sgdamore@opensolaris.org 		doreset = B_TRUE;
18959860Sgdamore@opensolaris.org 	}
18969860Sgdamore@opensolaris.org 
18979860Sgdamore@opensolaris.org 
18989860Sgdamore@opensolaris.org 	if (doreset) {
18994992Sgd78059 		mutex_enter(&afep->afe_xmtlock);
19004992Sgd78059 		afe_resetall(afep);
19014992Sgd78059 		mutex_exit(&afep->afe_xmtlock);
19029860Sgdamore@opensolaris.org 		mutex_exit(&afep->afe_intrlock);
19039860Sgdamore@opensolaris.org 
19049860Sgdamore@opensolaris.org 		mii_reset(afep->afe_mii);
19059860Sgdamore@opensolaris.org 	} else {
19069860Sgdamore@opensolaris.org 		mutex_exit(&afep->afe_intrlock);
19074992Sgd78059 	}
19084992Sgd78059 
19099860Sgdamore@opensolaris.org 	if (status & INT_LINKCHG) {
19109860Sgdamore@opensolaris.org 		mii_check(afep->afe_mii);
19119860Sgdamore@opensolaris.org 	}
19124992Sgd78059 
19134992Sgd78059 	/*
19144992Sgd78059 	 * Send up packets.  We do this outside of the intrlock.
19154992Sgd78059 	 */
19164992Sgd78059 	if (mp) {
19174992Sgd78059 		mac_rx(afep->afe_mh, NULL, mp);
19184992Sgd78059 	}
19194992Sgd78059 
19204992Sgd78059 	return (DDI_INTR_CLAIMED);
19214992Sgd78059 }
19224992Sgd78059 
19234992Sgd78059 void
afe_enableinterrupts(afe_t * afep)19244992Sgd78059 afe_enableinterrupts(afe_t *afep)
19254992Sgd78059 {
19264992Sgd78059 	unsigned mask = INT_WANTED;
19274992Sgd78059 
19284992Sgd78059 	if (afep->afe_wantw)
19294992Sgd78059 		mask |= INT_TXOK;
19304992Sgd78059 
19314992Sgd78059 	PUTCSR(afep, CSR_IER2, mask);
19324992Sgd78059 
19334992Sgd78059 	if (AFE_MODEL(afep) == MODEL_COMET) {
19344992Sgd78059 		/*
19354992Sgd78059 		 * On the Comet, this is the internal transceiver
19364992Sgd78059 		 * interrupt.  We program the Comet's built-in PHY to
19374992Sgd78059 		 * enable certain interrupts.
19384992Sgd78059 		 */
19394992Sgd78059 		PUTCSR16(afep, CSR_XIE, XIE_LDE | XIE_ANCE);
19404992Sgd78059 	}
19414992Sgd78059 }
19424992Sgd78059 
19434992Sgd78059 void
afe_disableinterrupts(afe_t * afep)19444992Sgd78059 afe_disableinterrupts(afe_t *afep)
19454992Sgd78059 {
19464992Sgd78059 	/* disable further interrupts */
19474992Sgd78059 	PUTCSR(afep, CSR_IER2, INT_NONE);
19484992Sgd78059 
19494992Sgd78059 	/* clear any pending interrupts */
19504992Sgd78059 	PUTCSR(afep, CSR_SR2, INT_ALL);
19514992Sgd78059 }
19524992Sgd78059 
19534992Sgd78059 boolean_t
afe_send(afe_t * afep,mblk_t * mp)19544992Sgd78059 afe_send(afe_t *afep, mblk_t *mp)
19554992Sgd78059 {
19564992Sgd78059 	size_t			len;
19574992Sgd78059 	afe_txbuf_t		*txb;
19584992Sgd78059 	afe_desc_t		*tmd;
19594992Sgd78059 	uint32_t		control;
19604992Sgd78059 	int			txsend;
19614992Sgd78059 
19624992Sgd78059 	ASSERT(mutex_owned(&afep->afe_xmtlock));
19634992Sgd78059 	ASSERT(mp != NULL);
19644992Sgd78059 
19654992Sgd78059 	len = msgsize(mp);
19664992Sgd78059 	if (len > ETHERVLANMTU) {
19674992Sgd78059 		afep->afe_macxmt_errors++;
19684992Sgd78059 		freemsg(mp);
19694992Sgd78059 		return (B_TRUE);
19704992Sgd78059 	}
19714992Sgd78059 
19724992Sgd78059 	if (afep->afe_txavail < AFE_TXRECLAIM)
19734992Sgd78059 		afe_reclaim(afep);
19744992Sgd78059 
19754992Sgd78059 	if (afep->afe_txavail == 0) {
19764992Sgd78059 		/* no more tmds */
19774992Sgd78059 		afep->afe_wantw = B_TRUE;
19784992Sgd78059 		/* enable TX interrupt */
19794992Sgd78059 		afe_enableinterrupts(afep);
19804992Sgd78059 		return (B_FALSE);
19814992Sgd78059 	}
19824992Sgd78059 
19834992Sgd78059 	txsend = afep->afe_txsend;
19844992Sgd78059 
19854992Sgd78059 	/*
19864992Sgd78059 	 * For simplicity, we just do a copy into a preallocated
19874992Sgd78059 	 * DMA buffer.
19884992Sgd78059 	 */
19894992Sgd78059 
19904992Sgd78059 	txb = afep->afe_txbufs[txsend];
19914992Sgd78059 	mcopymsg(mp, txb->txb_buf);	/* frees mp! */
19924992Sgd78059 
19934992Sgd78059 	/*
19944992Sgd78059 	 * Statistics.
19954992Sgd78059 	 */
19964992Sgd78059 	afep->afe_opackets++;
19974992Sgd78059 	afep->afe_obytes += len;
19984992Sgd78059 	if (txb->txb_buf[0] & 0x1) {
19994992Sgd78059 		if (bcmp(txb->txb_buf, afe_broadcast, ETHERADDRL) != 0)
20004992Sgd78059 			afep->afe_multixmt++;
20014992Sgd78059 		else
20024992Sgd78059 			afep->afe_brdcstxmt++;
20034992Sgd78059 	}
20044992Sgd78059 
20054992Sgd78059 	/* note len is already known to be a small unsigned */
20064992Sgd78059 	control = len | TXCTL_FIRST | TXCTL_LAST | TXCTL_INTCMPLTE;
20074992Sgd78059 
20084992Sgd78059 	if (txsend == (AFE_TXRING - 1))
20094992Sgd78059 		control |= TXCTL_ENDRING;
20104992Sgd78059 
20114992Sgd78059 	tmd = &afep->afe_txdescp[txsend];
20124992Sgd78059 
20134992Sgd78059 	SYNCTXBUF(txb, len, DDI_DMA_SYNC_FORDEV);
20144992Sgd78059 	PUTTXDESC(afep, tmd->desc_control, control);
20154992Sgd78059 	PUTTXDESC(afep, tmd->desc_buffer1, txb->txb_paddr);
20164992Sgd78059 	PUTTXDESC(afep, tmd->desc_buffer2, 0);
20174992Sgd78059 	PUTTXDESC(afep, tmd->desc_status, TXSTAT_OWN);
20184992Sgd78059 	/* sync the descriptor out to the device */
20194992Sgd78059 	SYNCTXDESC(afep, txsend, DDI_DMA_SYNC_FORDEV);
20204992Sgd78059 
20214992Sgd78059 	/*
20224992Sgd78059 	 * Note the new values of txavail and txsend.
20234992Sgd78059 	 */
20244992Sgd78059 	afep->afe_txavail--;
20254992Sgd78059 	afep->afe_txsend = (txsend + 1) % AFE_TXRING;
20264992Sgd78059 
20274992Sgd78059 	/*
20284992Sgd78059 	 * It should never, ever take more than 5 seconds to drain
20294992Sgd78059 	 * the ring.  If it happens, then we are stuck!
20304992Sgd78059 	 */
20314992Sgd78059 	afep->afe_txstall_time = gethrtime() + (5 * 1000000000ULL);
20324992Sgd78059 
20334992Sgd78059 	/*
20344992Sgd78059 	 * wake up the chip ... inside the lock to protect against DR suspend,
20354992Sgd78059 	 * etc.
20364992Sgd78059 	 */
20374992Sgd78059 	PUTCSR(afep, CSR_TDR, 0);
20384992Sgd78059 
20394992Sgd78059 	return (B_TRUE);
20404992Sgd78059 }
20414992Sgd78059 
20424992Sgd78059 /*
20434992Sgd78059  * Reclaim buffers that have completed transmission.
20444992Sgd78059  */
20454992Sgd78059 void
afe_reclaim(afe_t * afep)20464992Sgd78059 afe_reclaim(afe_t *afep)
20474992Sgd78059 {
20484992Sgd78059 	afe_desc_t	*tmdp;
20494992Sgd78059 
20504992Sgd78059 	while (afep->afe_txavail != AFE_TXRING) {
20514992Sgd78059 		uint32_t	status;
20524992Sgd78059 		uint32_t	control;
20534992Sgd78059 		int		index = afep->afe_txreclaim;
20544992Sgd78059 
20554992Sgd78059 		tmdp = &afep->afe_txdescp[index];
20564992Sgd78059 
20574992Sgd78059 		/* sync it before we read it */
20584992Sgd78059 		SYNCTXDESC(afep, index, DDI_DMA_SYNC_FORKERNEL);
20594992Sgd78059 
20604992Sgd78059 		control = GETTXDESC(afep, tmdp->desc_control);
20614992Sgd78059 		status = GETTXDESC(afep, tmdp->desc_status);
20624992Sgd78059 
20634992Sgd78059 		if (status & TXSTAT_OWN) {
20644992Sgd78059 			/* chip is still working on it, we're done */
20654992Sgd78059 			break;
20664992Sgd78059 		}
20674992Sgd78059 
20684992Sgd78059 		afep->afe_txavail++;
20694992Sgd78059 		afep->afe_txreclaim = (index + 1) % AFE_TXRING;
20704992Sgd78059 
20714992Sgd78059 		/* in the most common successful case, all bits are clear */
20724992Sgd78059 		if (status == 0)
20734992Sgd78059 			continue;
20744992Sgd78059 
20754992Sgd78059 		if ((control & TXCTL_LAST) == 0)
20764992Sgd78059 			continue;
20774992Sgd78059 
20784992Sgd78059 		if (status & TXSTAT_TXERR) {
20794992Sgd78059 			afep->afe_errxmt++;
20804992Sgd78059 
20814992Sgd78059 			if (status & TXSTAT_JABBER) {
20824992Sgd78059 				/* transmit jabber timeout */
20834992Sgd78059 				afep->afe_macxmt_errors++;
20844992Sgd78059 			}
20854992Sgd78059 			if (status &
20864992Sgd78059 			    (TXSTAT_CARRLOST | TXSTAT_NOCARR)) {
20874992Sgd78059 				afep->afe_carrier_errors++;
20884992Sgd78059 			}
20894992Sgd78059 			if (status & TXSTAT_UFLOW) {
20904992Sgd78059 				afep->afe_underflow++;
20914992Sgd78059 			}
20924992Sgd78059 			if (status & TXSTAT_LATECOL) {
20934992Sgd78059 				afep->afe_tx_late_collisions++;
20944992Sgd78059 			}
20954992Sgd78059 			if (status & TXSTAT_EXCOLL) {
20964992Sgd78059 				afep->afe_ex_collisions++;
20974992Sgd78059 				afep->afe_collisions += 16;
20984992Sgd78059 			}
20994992Sgd78059 		}
21004992Sgd78059 
21014992Sgd78059 		if (status & TXSTAT_DEFER) {
21024992Sgd78059 			afep->afe_defer_xmts++;
21034992Sgd78059 		}
21044992Sgd78059 
21054992Sgd78059 		/* collision counting */
21064992Sgd78059 		if (TXCOLLCNT(status) == 1) {
21074992Sgd78059 			afep->afe_collisions++;
21084992Sgd78059 			afep->afe_first_collisions++;
21094992Sgd78059 		} else if (TXCOLLCNT(status)) {
21104992Sgd78059 			afep->afe_collisions += TXCOLLCNT(status);
21114992Sgd78059 			afep->afe_multi_collisions += TXCOLLCNT(status);
21124992Sgd78059 		}
21134992Sgd78059 	}
21144992Sgd78059 
21154992Sgd78059 	if (afep->afe_txavail >= AFE_TXRESCHED) {
21164992Sgd78059 		if (afep->afe_wantw) {
21174992Sgd78059 			/*
21184992Sgd78059 			 * we were able to reclaim some packets, so
21194992Sgd78059 			 * disable tx interrupts
21204992Sgd78059 			 */
21214992Sgd78059 			afep->afe_wantw = B_FALSE;
21224992Sgd78059 			afe_enableinterrupts(afep);
21234992Sgd78059 			mac_tx_update(afep->afe_mh);
21244992Sgd78059 		}
21254992Sgd78059 	}
21264992Sgd78059 }
21274992Sgd78059 
21284992Sgd78059 mblk_t *
afe_receive(afe_t * afep)21294992Sgd78059 afe_receive(afe_t *afep)
21304992Sgd78059 {
21314992Sgd78059 	unsigned		len;
21324992Sgd78059 	afe_rxbuf_t		*rxb;
21334992Sgd78059 	afe_desc_t		*rmd;
21344992Sgd78059 	uint32_t		status;
21354992Sgd78059 	mblk_t			*mpchain, **mpp, *mp;
21364992Sgd78059 	int			head, cnt;
21374992Sgd78059 
21384992Sgd78059 	mpchain = NULL;
21394992Sgd78059 	mpp = &mpchain;
21404992Sgd78059 	head = afep->afe_rxhead;
21414992Sgd78059 
21424992Sgd78059 	/* limit the number of packets we process to a half ring size */
21434992Sgd78059 	for (cnt = 0; cnt < AFE_RXRING / 2; cnt++) {
21444992Sgd78059 
21454992Sgd78059 		rmd = &afep->afe_rxdescp[head];
21464992Sgd78059 		rxb = afep->afe_rxbufs[head];
21474992Sgd78059 
21484992Sgd78059 		SYNCRXDESC(afep, head, DDI_DMA_SYNC_FORKERNEL);
21494992Sgd78059 		status = GETRXDESC(afep, rmd->desc_status);
21504992Sgd78059 		if (status & RXSTAT_OWN) {
21514992Sgd78059 			/* chip is still chewing on it */
21524992Sgd78059 			break;
21534992Sgd78059 		}
21544992Sgd78059 
21554992Sgd78059 		/* discard the ethernet frame checksum */
21564992Sgd78059 		len = RXLENGTH(status) - ETHERFCSL;
21574992Sgd78059 
21584992Sgd78059 		if ((status & (RXSTAT_ERRS | RXSTAT_FIRST | RXSTAT_LAST)) !=
21594992Sgd78059 		    (RXSTAT_FIRST | RXSTAT_LAST)) {
21604992Sgd78059 
21614992Sgd78059 			afep->afe_errrcv++;
21624992Sgd78059 
21634992Sgd78059 			/*
21644992Sgd78059 			 * Abnormal status bits detected, analyze further.
21654992Sgd78059 			 */
21664992Sgd78059 			if ((status & (RXSTAT_LAST|RXSTAT_FIRST)) !=
21674992Sgd78059 			    (RXSTAT_LAST|RXSTAT_FIRST)) {
21689860Sgdamore@opensolaris.org 
21694992Sgd78059 				if (status & RXSTAT_FIRST) {
21704992Sgd78059 					afep->afe_toolong_errors++;
21714992Sgd78059 				}
21724992Sgd78059 			} else if (status & RXSTAT_DESCERR) {
21734992Sgd78059 				afep->afe_macrcv_errors++;
21744992Sgd78059 
21754992Sgd78059 			} else if (status & RXSTAT_RUNT) {
21764992Sgd78059 				afep->afe_runt++;
21774992Sgd78059 
21784992Sgd78059 			} else if (status & RXSTAT_COLLSEEN) {
21794992Sgd78059 				/* this should really be rx_late_collisions */
21804992Sgd78059 				afep->afe_macrcv_errors++;
21814992Sgd78059 
21824992Sgd78059 			} else if (status & RXSTAT_DRIBBLE) {
21834992Sgd78059 				afep->afe_align_errors++;
21844992Sgd78059 
21854992Sgd78059 			} else if (status & RXSTAT_CRCERR) {
21864992Sgd78059 				afep->afe_fcs_errors++;
21874992Sgd78059 
21884992Sgd78059 			} else if (status & RXSTAT_OFLOW) {
21894992Sgd78059 				afep->afe_overflow++;
21904992Sgd78059 			}
21914992Sgd78059 		}
21924992Sgd78059 
21934992Sgd78059 		else if (len > ETHERVLANMTU) {
21944992Sgd78059 			afep->afe_errrcv++;
21954992Sgd78059 			afep->afe_toolong_errors++;
21964992Sgd78059 		}
21974992Sgd78059 
21984992Sgd78059 		/*
21994992Sgd78059 		 * At this point, the chip thinks the packet is OK.
22004992Sgd78059 		 */
22014992Sgd78059 		else {
22024992Sgd78059 			mp = allocb(len + AFE_HEADROOM, 0);
22034992Sgd78059 			if (mp == NULL) {
22044992Sgd78059 				afep->afe_errrcv++;
22054992Sgd78059 				afep->afe_norcvbuf++;
22064992Sgd78059 				goto skip;
22074992Sgd78059 			}
22084992Sgd78059 
22094992Sgd78059 			/* sync the buffer before we look at it */
22104992Sgd78059 			SYNCRXBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
22114992Sgd78059 			mp->b_rptr += AFE_HEADROOM;
22124992Sgd78059 			mp->b_wptr = mp->b_rptr + len;
22134992Sgd78059 			bcopy((char *)rxb->rxb_buf, mp->b_rptr, len);
22144992Sgd78059 
22154992Sgd78059 			afep->afe_ipackets++;
22164992Sgd78059 			afep->afe_rbytes += len;
22174992Sgd78059 			if (status & RXSTAT_GROUP) {
22184992Sgd78059 				if (bcmp(mp->b_rptr, afe_broadcast,
22194992Sgd78059 				    ETHERADDRL) == 0)
22204992Sgd78059 					afep->afe_brdcstrcv++;
22214992Sgd78059 				else
22224992Sgd78059 					afep->afe_multircv++;
22234992Sgd78059 			}
22244992Sgd78059 			*mpp = mp;
22254992Sgd78059 			mpp = &mp->b_next;
22264992Sgd78059 		}
22274992Sgd78059 
22284992Sgd78059 skip:
22294992Sgd78059 		/* return ring entry to the hardware */
22304992Sgd78059 		PUTRXDESC(afep, rmd->desc_status, RXSTAT_OWN);
22314992Sgd78059 		SYNCRXDESC(afep, head, DDI_DMA_SYNC_FORDEV);
22324992Sgd78059 
22334992Sgd78059 		/* advance to next RMD */
22344992Sgd78059 		head = (head + 1) % AFE_RXRING;
22354992Sgd78059 	}
22364992Sgd78059 
22374992Sgd78059 	afep->afe_rxhead = head;
22384992Sgd78059 
22394992Sgd78059 	return (mpchain);
22404992Sgd78059 }
22414992Sgd78059 
22424992Sgd78059 int
afe_m_stat(void * arg,uint_t stat,uint64_t * val)22434992Sgd78059 afe_m_stat(void *arg, uint_t stat, uint64_t *val)
22444992Sgd78059 {
22454992Sgd78059 	afe_t	*afep = arg;
22464992Sgd78059 
22474992Sgd78059 	mutex_enter(&afep->afe_xmtlock);
22484992Sgd78059 	if ((afep->afe_flags & (AFE_RUNNING|AFE_SUSPENDED)) == AFE_RUNNING)
22494992Sgd78059 		afe_reclaim(afep);
22504992Sgd78059 	mutex_exit(&afep->afe_xmtlock);
22514992Sgd78059 
22529860Sgdamore@opensolaris.org 	if (mii_m_getstat(afep->afe_mii, stat, val) == 0) {
22539860Sgdamore@opensolaris.org 		return (0);
22549860Sgdamore@opensolaris.org 	}
22554992Sgd78059 	switch (stat) {
22564992Sgd78059 	case MAC_STAT_MULTIRCV:
22574992Sgd78059 		*val = afep->afe_multircv;
22584992Sgd78059 		break;
22594992Sgd78059 
22604992Sgd78059 	case MAC_STAT_BRDCSTRCV:
22614992Sgd78059 		*val = afep->afe_brdcstrcv;
22624992Sgd78059 		break;
22634992Sgd78059 
22644992Sgd78059 	case MAC_STAT_MULTIXMT:
22654992Sgd78059 		*val = afep->afe_multixmt;
22664992Sgd78059 		break;
22674992Sgd78059 
22684992Sgd78059 	case MAC_STAT_BRDCSTXMT:
22694992Sgd78059 		*val = afep->afe_brdcstxmt;
22704992Sgd78059 		break;
22714992Sgd78059 
22724992Sgd78059 	case MAC_STAT_IPACKETS:
22734992Sgd78059 		*val = afep->afe_ipackets;
22744992Sgd78059 		break;
22754992Sgd78059 
22764992Sgd78059 	case MAC_STAT_RBYTES:
22774992Sgd78059 		*val = afep->afe_rbytes;
22784992Sgd78059 		break;
22794992Sgd78059 
22804992Sgd78059 	case MAC_STAT_OPACKETS:
22814992Sgd78059 		*val = afep->afe_opackets;
22824992Sgd78059 		break;
22834992Sgd78059 
22844992Sgd78059 	case MAC_STAT_OBYTES:
22854992Sgd78059 		*val = afep->afe_obytes;
22864992Sgd78059 		break;
22874992Sgd78059 
22884992Sgd78059 	case MAC_STAT_NORCVBUF:
22894992Sgd78059 		*val = afep->afe_norcvbuf;
22904992Sgd78059 		break;
22914992Sgd78059 
22924992Sgd78059 	case MAC_STAT_NOXMTBUF:
22934992Sgd78059 		*val = 0;
22944992Sgd78059 		break;
22954992Sgd78059 
22964992Sgd78059 	case MAC_STAT_COLLISIONS:
22974992Sgd78059 		*val = afep->afe_collisions;
22984992Sgd78059 		break;
22994992Sgd78059 
23004992Sgd78059 	case MAC_STAT_IERRORS:
23014992Sgd78059 		*val = afep->afe_errrcv;
23024992Sgd78059 		break;
23034992Sgd78059 
23044992Sgd78059 	case MAC_STAT_OERRORS:
23054992Sgd78059 		*val = afep->afe_errxmt;
23064992Sgd78059 		break;
23074992Sgd78059 
23084992Sgd78059 	case ETHER_STAT_ALIGN_ERRORS:
23094992Sgd78059 		*val = afep->afe_align_errors;
23104992Sgd78059 		break;
23114992Sgd78059 
23124992Sgd78059 	case ETHER_STAT_FCS_ERRORS:
23134992Sgd78059 		*val = afep->afe_fcs_errors;
23144992Sgd78059 		break;
23154992Sgd78059 
23164992Sgd78059 	case ETHER_STAT_SQE_ERRORS:
23174992Sgd78059 		*val = afep->afe_sqe_errors;
23184992Sgd78059 		break;
23194992Sgd78059 
23204992Sgd78059 	case ETHER_STAT_DEFER_XMTS:
23214992Sgd78059 		*val = afep->afe_defer_xmts;
23224992Sgd78059 		break;
23234992Sgd78059 
23244992Sgd78059 	case ETHER_STAT_FIRST_COLLISIONS:
23254992Sgd78059 		*val = afep->afe_first_collisions;
23264992Sgd78059 		break;
23274992Sgd78059 
23284992Sgd78059 	case ETHER_STAT_MULTI_COLLISIONS:
23294992Sgd78059 		*val = afep->afe_multi_collisions;
23304992Sgd78059 		break;
23314992Sgd78059 
23324992Sgd78059 	case ETHER_STAT_TX_LATE_COLLISIONS:
23334992Sgd78059 		*val = afep->afe_tx_late_collisions;
23344992Sgd78059 		break;
23354992Sgd78059 
23364992Sgd78059 	case ETHER_STAT_EX_COLLISIONS:
23374992Sgd78059 		*val = afep->afe_ex_collisions;
23384992Sgd78059 		break;
23394992Sgd78059 
23404992Sgd78059 	case ETHER_STAT_MACXMT_ERRORS:
23414992Sgd78059 		*val = afep->afe_macxmt_errors;
23424992Sgd78059 		break;
23434992Sgd78059 
23444992Sgd78059 	case ETHER_STAT_CARRIER_ERRORS:
23454992Sgd78059 		*val = afep->afe_carrier_errors;
23464992Sgd78059 		break;
23474992Sgd78059 
23484992Sgd78059 	case ETHER_STAT_TOOLONG_ERRORS:
23494992Sgd78059 		*val = afep->afe_toolong_errors;
23504992Sgd78059 		break;
23514992Sgd78059 
23524992Sgd78059 	case ETHER_STAT_MACRCV_ERRORS:
23534992Sgd78059 		*val = afep->afe_macrcv_errors;
23544992Sgd78059 		break;
23554992Sgd78059 
23564992Sgd78059 	case MAC_STAT_OVERFLOWS:
23574992Sgd78059 		*val = afep->afe_overflow;
23584992Sgd78059 		break;
23594992Sgd78059 
23604992Sgd78059 	case MAC_STAT_UNDERFLOWS:
23614992Sgd78059 		*val = afep->afe_underflow;
23624992Sgd78059 		break;
23634992Sgd78059 
23644992Sgd78059 	case ETHER_STAT_TOOSHORT_ERRORS:
23654992Sgd78059 		*val = afep->afe_runt;
23664992Sgd78059 		break;
23674992Sgd78059 
23684992Sgd78059 	case ETHER_STAT_JABBER_ERRORS:
23694992Sgd78059 		*val = afep->afe_jabber;
23704992Sgd78059 		break;
23714992Sgd78059 
23724992Sgd78059 	default:
23734992Sgd78059 		return (ENOTSUP);
23744992Sgd78059 	}
23754992Sgd78059 	return (0);
23764992Sgd78059 }
23774992Sgd78059 
23786684Sgd78059 int
afe_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)2379*11878SVenu.Iyer@Sun.COM afe_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
2380*11878SVenu.Iyer@Sun.COM     void *val)
23814992Sgd78059 {
23826684Sgd78059 	afe_t		*afep = arg;
23836684Sgd78059 
2384*11878SVenu.Iyer@Sun.COM 	return (mii_m_getprop(afep->afe_mii, name, num, sz, val));
23854992Sgd78059 }
23864992Sgd78059 
23874992Sgd78059 int
afe_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)23886684Sgd78059 afe_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
23896684Sgd78059     const void *val)
23904992Sgd78059 {
23916684Sgd78059 	afe_t		*afep = arg;
23926684Sgd78059 
23939860Sgdamore@opensolaris.org 	return (mii_m_setprop(afep->afe_mii, name, num, sz, val));
23944992Sgd78059 }
23954992Sgd78059 
2396*11878SVenu.Iyer@Sun.COM static void
afe_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t prh)2397*11878SVenu.Iyer@Sun.COM afe_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
2398*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
2399*11878SVenu.Iyer@Sun.COM {
2400*11878SVenu.Iyer@Sun.COM 	afe_t		*afep = arg;
2401*11878SVenu.Iyer@Sun.COM 
2402*11878SVenu.Iyer@Sun.COM 	mii_m_propinfo(afep->afe_mii, name, num, prh);
2403*11878SVenu.Iyer@Sun.COM }
2404*11878SVenu.Iyer@Sun.COM 
24054992Sgd78059 /*
24064992Sgd78059  * Debugging and error reporting.
24074992Sgd78059  */
24084992Sgd78059 void
afe_error(dev_info_t * dip,char * fmt,...)24094992Sgd78059 afe_error(dev_info_t *dip, char *fmt, ...)
24104992Sgd78059 {
24114992Sgd78059 	va_list	ap;
24124992Sgd78059 	char	buf[256];
24134992Sgd78059 
24144992Sgd78059 	va_start(ap, fmt);
24154992Sgd78059 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
24164992Sgd78059 	va_end(ap);
24174992Sgd78059 
24184992Sgd78059 	if (dip) {
24194992Sgd78059 		cmn_err(CE_WARN, "%s%d: %s",
24204992Sgd78059 		    ddi_driver_name(dip), ddi_get_instance(dip), buf);
24214992Sgd78059 	} else {
24224992Sgd78059 		cmn_err(CE_WARN, "afe: %s", buf);
24234992Sgd78059 	}
24244992Sgd78059 }
2425