xref: /illumos-gate/usr/src/uts/common/io/vr/vr.c (revision cfe080a1)
12ca5b659SJoost Mulders /*
22ca5b659SJoost Mulders  * CDDL HEADER START
32ca5b659SJoost Mulders  *
42ca5b659SJoost Mulders  * The contents of this file are subject to the terms of the
52ca5b659SJoost Mulders  * Common Development and Distribution License (the "License").
62ca5b659SJoost Mulders  * You may not use this file except in compliance with the License.
72ca5b659SJoost Mulders  *
82ca5b659SJoost Mulders  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92ca5b659SJoost Mulders  * or http://www.opensolaris.org/os/licensing.
102ca5b659SJoost Mulders  * See the License for the specific language governing permissions
112ca5b659SJoost Mulders  * and limitations under the License.
122ca5b659SJoost Mulders  *
132ca5b659SJoost Mulders  * When distributing Covered Code, include this CDDL HEADER in each
142ca5b659SJoost Mulders  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152ca5b659SJoost Mulders  * If applicable, add the following below this CDDL HEADER, with the
162ca5b659SJoost Mulders  * fields enclosed by brackets "[]" replaced with your own identifying
172ca5b659SJoost Mulders  * information: Portions Copyright [yyyy] [name of copyright owner]
182ca5b659SJoost Mulders  *
192ca5b659SJoost Mulders  * CDDL HEADER END
202ca5b659SJoost Mulders  */
212ca5b659SJoost Mulders 
222ca5b659SJoost Mulders /*
230dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
242ca5b659SJoost Mulders  * Use is subject to license terms.
252ca5b659SJoost Mulders  */
262ca5b659SJoost Mulders 
2715c07adcSJohn Levon /*
2815c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
2915c07adcSJohn Levon  */
3015c07adcSJohn Levon 
312ca5b659SJoost Mulders #include <sys/types.h>
322ca5b659SJoost Mulders #include <sys/stream.h>
332ca5b659SJoost Mulders #include <sys/strsun.h>
342ca5b659SJoost Mulders #include <sys/stat.h>
352ca5b659SJoost Mulders #include <sys/pci.h>
362ca5b659SJoost Mulders #include <sys/modctl.h>
372ca5b659SJoost Mulders #include <sys/kstat.h>
382ca5b659SJoost Mulders #include <sys/ethernet.h>
392ca5b659SJoost Mulders #include <sys/devops.h>
402ca5b659SJoost Mulders #include <sys/debug.h>
412ca5b659SJoost Mulders #include <sys/conf.h>
422ca5b659SJoost Mulders #include <sys/mac.h>
432ca5b659SJoost Mulders #include <sys/mac_provider.h>
442ca5b659SJoost Mulders #include <sys/mac_ether.h>
452ca5b659SJoost Mulders #include <sys/sysmacros.h>
462ca5b659SJoost Mulders #include <sys/dditypes.h>
472ca5b659SJoost Mulders #include <sys/ddi.h>
482ca5b659SJoost Mulders #include <sys/sunddi.h>
492ca5b659SJoost Mulders #include <sys/miiregs.h>
502ca5b659SJoost Mulders #include <sys/byteorder.h>
512ca5b659SJoost Mulders #include <sys/note.h>
522ca5b659SJoost Mulders #include <sys/vlan.h>
532ca5b659SJoost Mulders 
542ca5b659SJoost Mulders #include "vr.h"
552ca5b659SJoost Mulders #include "vr_impl.h"
562ca5b659SJoost Mulders 
572ca5b659SJoost Mulders /*
582ca5b659SJoost Mulders  * VR in a nutshell
592ca5b659SJoost Mulders  * The card uses two rings of data structures to communicate with the host.
602ca5b659SJoost Mulders  * These are referred to as "descriptor rings" and there is one for transmit
612ca5b659SJoost Mulders  * (TX) and one for receive (RX).
622ca5b659SJoost Mulders  *
632ca5b659SJoost Mulders  * The driver uses a "DMA buffer" data type for mapping to those descriptor
642ca5b659SJoost Mulders  * rings. This is a structure with handles and a DMA'able buffer attached to it.
652ca5b659SJoost Mulders  *
662ca5b659SJoost Mulders  * Receive
672ca5b659SJoost Mulders  * The receive ring is filled with DMA buffers. Received packets are copied into
682ca5b659SJoost Mulders  * a newly allocated mblk's and passed upstream.
692ca5b659SJoost Mulders  *
702ca5b659SJoost Mulders  * Transmit
712ca5b659SJoost Mulders  * Each transmit descriptor has a DMA buffer attached to it. The data of TX
722ca5b659SJoost Mulders  * packets is copied into the DMA buffer which is then enqueued for
732ca5b659SJoost Mulders  * transmission.
742ca5b659SJoost Mulders  *
752ca5b659SJoost Mulders  * Reclaim of transmitted packets is done as a result of a transmit completion
762ca5b659SJoost Mulders  * interrupt which is generated 3 times per ring at minimum.
772ca5b659SJoost Mulders  */
782ca5b659SJoost Mulders 
792ca5b659SJoost Mulders #if defined(DEBUG)
802ca5b659SJoost Mulders uint32_t	vrdebug = 1;
812ca5b659SJoost Mulders #define	VR_DEBUG(args)	do {				\
822ca5b659SJoost Mulders 		if (vrdebug > 0)			\
832ca5b659SJoost Mulders 			(*vr_debug()) args;		\
842ca5b659SJoost Mulders 			_NOTE(CONSTANTCONDITION)	\
852ca5b659SJoost Mulders 		} while (0)
862ca5b659SJoost Mulders static	void	vr_prt(const char *fmt, ...);
872ca5b659SJoost Mulders 	void	(*vr_debug())(const char *fmt, ...);
882ca5b659SJoost Mulders #else
892ca5b659SJoost Mulders #define	VR_DEBUG(args)	do ; _NOTE(CONSTANTCONDITION) while (0)
902ca5b659SJoost Mulders #endif
912ca5b659SJoost Mulders 
925815e35bSjoostmnl@gmail.com static char vr_ident[] = "VIA Rhine Ethernet";
932ca5b659SJoost Mulders 
942ca5b659SJoost Mulders /*
952ca5b659SJoost Mulders  * Attributes for accessing registers and memory descriptors for this device.
962ca5b659SJoost Mulders  */
972ca5b659SJoost Mulders static ddi_device_acc_attr_t vr_dev_dma_accattr = {
982ca5b659SJoost Mulders 	DDI_DEVICE_ATTR_V0,
992ca5b659SJoost Mulders 	DDI_STRUCTURE_LE_ACC,
1002ca5b659SJoost Mulders 	DDI_STRICTORDER_ACC
1012ca5b659SJoost Mulders };
1022ca5b659SJoost Mulders 
1032ca5b659SJoost Mulders /*
1042ca5b659SJoost Mulders  * Attributes for accessing data.
1052ca5b659SJoost Mulders  */
1062ca5b659SJoost Mulders static ddi_device_acc_attr_t vr_data_dma_accattr = {
1072ca5b659SJoost Mulders 	DDI_DEVICE_ATTR_V0,
1082ca5b659SJoost Mulders 	DDI_NEVERSWAP_ACC,
1092ca5b659SJoost Mulders 	DDI_STRICTORDER_ACC
1102ca5b659SJoost Mulders };
1112ca5b659SJoost Mulders 
1122ca5b659SJoost Mulders /*
1132ca5b659SJoost Mulders  * DMA attributes for descriptors for communication with the device
1142ca5b659SJoost Mulders  * This driver assumes that all descriptors of one ring fit in one consequitive
1152ca5b659SJoost Mulders  * memory area of max 4K (256 descriptors) that does not cross a page boundary.
1162ca5b659SJoost Mulders  * Therefore, we request 4K alignement.
1172ca5b659SJoost Mulders  */
1182ca5b659SJoost Mulders static ddi_dma_attr_t vr_dev_dma_attr = {
1192ca5b659SJoost Mulders 	DMA_ATTR_V0,			/* version number */
1202ca5b659SJoost Mulders 	0,				/* low DMA address range */
1212ca5b659SJoost Mulders 	0xFFFFFFFF,			/* high DMA address range */
1222ca5b659SJoost Mulders 	0x7FFFFFFF,			/* DMA counter register */
1232ca5b659SJoost Mulders 	0x1000,				/* DMA address alignment */
1242ca5b659SJoost Mulders 	0x7F,				/* DMA burstsizes */
1252ca5b659SJoost Mulders 	1,				/* min effective DMA size */
1262ca5b659SJoost Mulders 	0xFFFFFFFF,			/* max DMA xfer size */
1272ca5b659SJoost Mulders 	0xFFFFFFFF,			/* segment boundary */
1282ca5b659SJoost Mulders 	1,				/* s/g list length */
1292ca5b659SJoost Mulders 	1,				/* granularity of device */
1302ca5b659SJoost Mulders 	0				/* DMA transfer flags */
1312ca5b659SJoost Mulders };
1322ca5b659SJoost Mulders 
1332ca5b659SJoost Mulders /*
1342ca5b659SJoost Mulders  * DMA attributes for the data moved to/from the device
1352ca5b659SJoost Mulders  * Note that the alignement is set to 2K so hat a 1500 byte packet never
1362ca5b659SJoost Mulders  * crosses a page boundary and thus that a DMA transfer is not split up in
1372ca5b659SJoost Mulders  * multiple cookies with a 4K/8K pagesize
1382ca5b659SJoost Mulders  */
1392ca5b659SJoost Mulders static ddi_dma_attr_t vr_data_dma_attr = {
1402ca5b659SJoost Mulders 	DMA_ATTR_V0,			/* version number */
1412ca5b659SJoost Mulders 	0,				/* low DMA address range */
1422ca5b659SJoost Mulders 	0xFFFFFFFF,			/* high DMA address range */
1432ca5b659SJoost Mulders 	0x7FFFFFFF,			/* DMA counter register */
1442ca5b659SJoost Mulders 	0x800,				/* DMA address alignment */
1452ca5b659SJoost Mulders 	0xfff,				/* DMA burstsizes */
1462ca5b659SJoost Mulders 	1,				/* min effective DMA size */
1472ca5b659SJoost Mulders 	0xFFFFFFFF,			/* max DMA xfer size */
1482ca5b659SJoost Mulders 	0xFFFFFFFF,			/* segment boundary */
1492ca5b659SJoost Mulders 	1,				/* s/g list length */
1502ca5b659SJoost Mulders 	1,				/* granularity of device */
1512ca5b659SJoost Mulders 	0				/* DMA transfer flags */
1522ca5b659SJoost Mulders };
1532ca5b659SJoost Mulders 
1542ca5b659SJoost Mulders static mac_callbacks_t vr_mac_callbacks = {
1550dc2366fSVenugopal Iyer 	MC_SETPROP|MC_GETPROP|MC_PROPINFO, /* Which callbacks are set */
1562ca5b659SJoost Mulders 	vr_mac_getstat,		/* Get the value of a statistic */
1572ca5b659SJoost Mulders 	vr_mac_start,		/* Start the device */
1582ca5b659SJoost Mulders 	vr_mac_stop,		/* Stop the device */
1592ca5b659SJoost Mulders 	vr_mac_set_promisc,	/* Enable or disable promiscuous mode */
1602ca5b659SJoost Mulders 	vr_mac_set_multicast,	/* Enable or disable a multicast addr */
1612ca5b659SJoost Mulders 	vr_mac_set_ether_addr,	/* Set the unicast MAC address */
1622ca5b659SJoost Mulders 	vr_mac_tx_enqueue_list,	/* Transmit a packet */
1630dc2366fSVenugopal Iyer 	NULL,
1642ca5b659SJoost Mulders 	NULL,			/* Process an unknown ioctl */
1652ca5b659SJoost Mulders 	NULL,			/* Get capability information */
1662ca5b659SJoost Mulders 	NULL,			/* Open the device */
1672ca5b659SJoost Mulders 	NULL,			/* Close the device */
1682ca5b659SJoost Mulders 	vr_mac_setprop,		/* Set properties of the device */
1690dc2366fSVenugopal Iyer 	vr_mac_getprop,		/* Get properties of the device */
1700dc2366fSVenugopal Iyer 	vr_mac_propinfo		/* Get properties attributes */
1712ca5b659SJoost Mulders };
1722ca5b659SJoost Mulders 
1732ca5b659SJoost Mulders /*
1742ca5b659SJoost Mulders  * Table with bugs and features for each incarnation of the card.
1752ca5b659SJoost Mulders  */
1762ca5b659SJoost Mulders static const chip_info_t vr_chip_info [] = {
1772ca5b659SJoost Mulders 	{
1782ca5b659SJoost Mulders 		0x0, 0x0,
1792ca5b659SJoost Mulders 		"VIA Rhine Fast Ethernet",
1802ca5b659SJoost Mulders 		(VR_BUG_NO_MEMIO),
1812ca5b659SJoost Mulders 		(VR_FEATURE_NONE)
1822ca5b659SJoost Mulders 	},
1832ca5b659SJoost Mulders 	{
1842ca5b659SJoost Mulders 		0x04, 0x21,
1852ca5b659SJoost Mulders 		"VIA VT86C100A Fast Ethernet",
1862ca5b659SJoost Mulders 		(VR_BUG_NEEDMODE2PCEROPT | VR_BUG_NO_TXQUEUEING |
1872ca5b659SJoost Mulders 		    VR_BUG_NEEDMODE10T | VR_BUG_TXALIGN | VR_BUG_NO_MEMIO |
1882ca5b659SJoost Mulders 		    VR_BUG_MIIPOLLSTOP),
1892ca5b659SJoost Mulders 		(VR_FEATURE_NONE)
1902ca5b659SJoost Mulders 	},
1912ca5b659SJoost Mulders 	{
1922ca5b659SJoost Mulders 		0x40, 0x41,
1932ca5b659SJoost Mulders 		"VIA VT6102-A Rhine II Fast Ethernet",
1942ca5b659SJoost Mulders 		(VR_BUG_NEEDMODE2PCEROPT),
1952ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP)
1962ca5b659SJoost Mulders 	},
1972ca5b659SJoost Mulders 	{
1982ca5b659SJoost Mulders 		0x42, 0x7f,
1992ca5b659SJoost Mulders 		"VIA VT6102-C Rhine II Fast Ethernet",
2002ca5b659SJoost Mulders 		(VR_BUG_NEEDMODE2PCEROPT),
2012ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP)
2022ca5b659SJoost Mulders 	},
2032ca5b659SJoost Mulders 	{
2042ca5b659SJoost Mulders 		0x80, 0x82,
2052ca5b659SJoost Mulders 		"VIA VT6105-A Rhine III Fast Ethernet",
2062ca5b659SJoost Mulders 		(VR_BUG_NONE),
2072ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
2082ca5b659SJoost Mulders 	},
2092ca5b659SJoost Mulders 	{
2102ca5b659SJoost Mulders 		0x83, 0x89,
2112ca5b659SJoost Mulders 		"VIA VT6105-B Rhine III Fast Ethernet",
2122ca5b659SJoost Mulders 		(VR_BUG_NONE),
2132ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
2142ca5b659SJoost Mulders 	},
2152ca5b659SJoost Mulders 	{
2162ca5b659SJoost Mulders 		0x8a, 0x8b,
2172ca5b659SJoost Mulders 		"VIA VT6105-LOM Rhine III Fast Ethernet",
2182ca5b659SJoost Mulders 		(VR_BUG_NONE),
2192ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
2202ca5b659SJoost Mulders 	},
2212ca5b659SJoost Mulders 	{
2222ca5b659SJoost Mulders 		0x8c, 0x8c,
2232ca5b659SJoost Mulders 		"VIA VT6107-A0 Rhine III Fast Ethernet",
2242ca5b659SJoost Mulders 		(VR_BUG_NONE),
2252ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP)
2262ca5b659SJoost Mulders 	},
2272ca5b659SJoost Mulders 	{
2282ca5b659SJoost Mulders 		0x8d, 0x8f,
2292ca5b659SJoost Mulders 		"VIA VT6107-A1 Rhine III Fast Ethernet",
2302ca5b659SJoost Mulders 		(VR_BUG_NONE),
2312ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
2322ca5b659SJoost Mulders 		    VR_FEATURE_MRDLNMULTIPLE)
2332ca5b659SJoost Mulders 	},
2342ca5b659SJoost Mulders 	{
2352ca5b659SJoost Mulders 		0x90, 0x93,
2362ca5b659SJoost Mulders 		"VIA VT6105M-A0 Rhine III Fast Ethernet Management Adapter",
2372ca5b659SJoost Mulders 		(VR_BUG_NONE),
2382ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
2392ca5b659SJoost Mulders 		    VR_FEATURE_TXCHKSUM | VR_FEATURE_RXCHKSUM |
2402ca5b659SJoost Mulders 		    VR_FEATURE_CAMSUPPORT | VR_FEATURE_VLANTAGGING |
2412ca5b659SJoost Mulders 		    VR_FEATURE_MIBCOUNTER)
2422ca5b659SJoost Mulders 	},
2432ca5b659SJoost Mulders 	{
2442ca5b659SJoost Mulders 		0x94, 0xff,
2452ca5b659SJoost Mulders 		"VIA VT6105M-B1 Rhine III Fast Ethernet Management Adapter",
2462ca5b659SJoost Mulders 		(VR_BUG_NONE),
2472ca5b659SJoost Mulders 		(VR_FEATURE_RX_PAUSE_CAP | VR_FEATURE_TX_PAUSE_CAP |
2482ca5b659SJoost Mulders 		    VR_FEATURE_TXCHKSUM | VR_FEATURE_RXCHKSUM |
2492ca5b659SJoost Mulders 		    VR_FEATURE_CAMSUPPORT | VR_FEATURE_VLANTAGGING |
2502ca5b659SJoost Mulders 		    VR_FEATURE_MIBCOUNTER)
2512ca5b659SJoost Mulders 	}
2522ca5b659SJoost Mulders };
2532ca5b659SJoost Mulders 
2542ca5b659SJoost Mulders /*
2552ca5b659SJoost Mulders  * Function prototypes
2562ca5b659SJoost Mulders  */
2572ca5b659SJoost Mulders static	vr_result_t	vr_add_intr(vr_t *vrp);
2582ca5b659SJoost Mulders static	void		vr_remove_intr(vr_t *vrp);
2592ca5b659SJoost Mulders static	int32_t		vr_cam_index(vr_t *vrp, const uint8_t *maddr);
2602ca5b659SJoost Mulders static	uint32_t	ether_crc_be(const uint8_t *address);
2612ca5b659SJoost Mulders static	void		vr_tx_enqueue_msg(vr_t *vrp, mblk_t *mp);
2622ca5b659SJoost Mulders static	void		vr_log(vr_t *vrp, int level, const char *fmt, ...);
2632ca5b659SJoost Mulders static	int		vr_resume(dev_info_t *devinfo);
2642ca5b659SJoost Mulders static	int		vr_suspend(dev_info_t *devinfo);
2652ca5b659SJoost Mulders static	vr_result_t	vr_bus_config(vr_t *vrp);
2662ca5b659SJoost Mulders static	void		vr_bus_unconfig(vr_t *vrp);
2672ca5b659SJoost Mulders static	void		vr_reset(vr_t *vrp);
2682ca5b659SJoost Mulders static	int		vr_start(vr_t *vrp);
2692ca5b659SJoost Mulders static	int		vr_stop(vr_t *vrp);
2702ca5b659SJoost Mulders static	vr_result_t	vr_rings_init(vr_t *vrp);
2712ca5b659SJoost Mulders static	void		vr_rings_fini(vr_t *vrp);
2722ca5b659SJoost Mulders static	vr_result_t	vr_alloc_ring(vr_t *vrp, vr_ring_t *r, size_t n);
2732ca5b659SJoost Mulders static	void		vr_free_ring(vr_ring_t *r, size_t n);
2742ca5b659SJoost Mulders static	vr_result_t	vr_rxring_init(vr_t *vrp);
2752ca5b659SJoost Mulders static	void		vr_rxring_fini(vr_t *vrp);
2762ca5b659SJoost Mulders static	vr_result_t	vr_txring_init(vr_t *vrp);
2772ca5b659SJoost Mulders static	void		vr_txring_fini(vr_t *vrp);
2782ca5b659SJoost Mulders static	vr_result_t	vr_alloc_dmabuf(vr_t *vrp, vr_data_dma_t *dmap,
2792ca5b659SJoost Mulders 			    uint_t flags);
2802ca5b659SJoost Mulders static	void		vr_free_dmabuf(vr_data_dma_t *dmap);
2812ca5b659SJoost Mulders static	void		vr_param_init(vr_t *vrp);
2822ca5b659SJoost Mulders static	mblk_t		*vr_receive(vr_t *vrp);
2832ca5b659SJoost Mulders static	void		vr_tx_reclaim(vr_t *vrp);
2842ca5b659SJoost Mulders static	void		vr_periodic(void *p);
2852ca5b659SJoost Mulders static	void		vr_error(vr_t *vrp);
2862ca5b659SJoost Mulders static	void		vr_phy_read(vr_t *vrp, int offset, uint16_t *value);
2872ca5b659SJoost Mulders static	void		vr_phy_write(vr_t *vrp, int offset, uint16_t value);
2882ca5b659SJoost Mulders static	void		vr_phy_autopoll_disable(vr_t *vrp);
2892ca5b659SJoost Mulders static	void		vr_phy_autopoll_enable(vr_t *vrp);
2902ca5b659SJoost Mulders static	void		vr_link_init(vr_t *vrp);
2912ca5b659SJoost Mulders static	void		vr_link_state(vr_t *vrp);
2922ca5b659SJoost Mulders static	void		vr_kstats_init(vr_t *vrp);
2932ca5b659SJoost Mulders static	int		vr_update_kstats(kstat_t *ksp, int access);
2942ca5b659SJoost Mulders static	void		vr_remove_kstats(vr_t *vrp);
2952ca5b659SJoost Mulders 
2962ca5b659SJoost Mulders static int
vr_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)2972ca5b659SJoost Mulders vr_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
2982ca5b659SJoost Mulders {
2992ca5b659SJoost Mulders 	vr_t		*vrp;
3002ca5b659SJoost Mulders 	mac_register_t	*macreg;
3012ca5b659SJoost Mulders 
3022ca5b659SJoost Mulders 	if (cmd == DDI_RESUME)
3032ca5b659SJoost Mulders 		return (vr_resume(devinfo));
3042ca5b659SJoost Mulders 	else if (cmd != DDI_ATTACH)
3052ca5b659SJoost Mulders 		return (DDI_FAILURE);
3062ca5b659SJoost Mulders 
3072ca5b659SJoost Mulders 	/*
3082ca5b659SJoost Mulders 	 * Attach.
3092ca5b659SJoost Mulders 	 */
3102ca5b659SJoost Mulders 	vrp = kmem_zalloc(sizeof (vr_t), KM_SLEEP);
3112ca5b659SJoost Mulders 	ddi_set_driver_private(devinfo, vrp);
3122ca5b659SJoost Mulders 	vrp->devinfo = devinfo;
3132ca5b659SJoost Mulders 
3142ca5b659SJoost Mulders 	/*
3152ca5b659SJoost Mulders 	 * Store the name+instance of the module.
3162ca5b659SJoost Mulders 	 */
3172ca5b659SJoost Mulders 	(void) snprintf(vrp->ifname, sizeof (vrp->ifname), "%s%d",
3182ca5b659SJoost Mulders 	    MODULENAME, ddi_get_instance(devinfo));
3192ca5b659SJoost Mulders 
3202ca5b659SJoost Mulders 	/*
3212ca5b659SJoost Mulders 	 * Bus initialization.
3222ca5b659SJoost Mulders 	 */
3232ca5b659SJoost Mulders 	if (vr_bus_config(vrp) != VR_SUCCESS) {
3242ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "vr_bus_config failed");
3252ca5b659SJoost Mulders 		goto fail0;
3262ca5b659SJoost Mulders 	}
3272ca5b659SJoost Mulders 
3282ca5b659SJoost Mulders 	/*
3292ca5b659SJoost Mulders 	 * Initialize default parameters.
3302ca5b659SJoost Mulders 	 */
3312ca5b659SJoost Mulders 	vr_param_init(vrp);
3322ca5b659SJoost Mulders 
3332ca5b659SJoost Mulders 	/*
3342ca5b659SJoost Mulders 	 * Setup the descriptor rings.
3352ca5b659SJoost Mulders 	 */
3362ca5b659SJoost Mulders 	if (vr_rings_init(vrp) != VR_SUCCESS) {
3372ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "vr_rings_init failed");
3382ca5b659SJoost Mulders 		goto fail1;
3392ca5b659SJoost Mulders 	}
3402ca5b659SJoost Mulders 
3412ca5b659SJoost Mulders 	/*
3422ca5b659SJoost Mulders 	 * Initialize kstats.
3432ca5b659SJoost Mulders 	 */
3442ca5b659SJoost Mulders 	vr_kstats_init(vrp);
3452ca5b659SJoost Mulders 
3462ca5b659SJoost Mulders 	/*
3472ca5b659SJoost Mulders 	 * Add interrupt to the OS.
3482ca5b659SJoost Mulders 	 */
3492ca5b659SJoost Mulders 	if (vr_add_intr(vrp) != VR_SUCCESS) {
3502ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "vr_add_intr failed in attach");
3512ca5b659SJoost Mulders 		goto fail3;
3522ca5b659SJoost Mulders 	}
3532ca5b659SJoost Mulders 
3542ca5b659SJoost Mulders 	/*
3552ca5b659SJoost Mulders 	 * Add mutexes.
3562ca5b659SJoost Mulders 	 */
3572ca5b659SJoost Mulders 	mutex_init(&vrp->intrlock, NULL, MUTEX_DRIVER,
3582ca5b659SJoost Mulders 	    DDI_INTR_PRI(vrp->intr_pri));
3592ca5b659SJoost Mulders 	mutex_init(&vrp->oplock, NULL, MUTEX_DRIVER, NULL);
3602ca5b659SJoost Mulders 	mutex_init(&vrp->tx.lock, NULL, MUTEX_DRIVER, NULL);
3612ca5b659SJoost Mulders 
3622ca5b659SJoost Mulders 	/*
3632ca5b659SJoost Mulders 	 * Enable interrupt.
3642ca5b659SJoost Mulders 	 */
3652ca5b659SJoost Mulders 	if (ddi_intr_enable(vrp->intr_hdl) != DDI_SUCCESS) {
3662ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_enable failed");
3672ca5b659SJoost Mulders 		goto fail5;
3682ca5b659SJoost Mulders 	}
3692ca5b659SJoost Mulders 
3702ca5b659SJoost Mulders 	/*
3712ca5b659SJoost Mulders 	 * Register with parent, mac.
3722ca5b659SJoost Mulders 	 */
3732ca5b659SJoost Mulders 	if ((macreg = mac_alloc(MAC_VERSION)) == NULL) {
3742ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "mac_alloc failed in attach");
3752ca5b659SJoost Mulders 		goto fail6;
3762ca5b659SJoost Mulders 	}
3772ca5b659SJoost Mulders 
3782ca5b659SJoost Mulders 	macreg->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
3792ca5b659SJoost Mulders 	macreg->m_driver = vrp;
3802ca5b659SJoost Mulders 	macreg->m_dip = devinfo;
3812ca5b659SJoost Mulders 	macreg->m_src_addr = vrp->vendor_ether_addr;
3822ca5b659SJoost Mulders 	macreg->m_callbacks = &vr_mac_callbacks;
3832ca5b659SJoost Mulders 	macreg->m_min_sdu = 0;
3842ca5b659SJoost Mulders 	macreg->m_max_sdu = ETHERMTU;
3852ca5b659SJoost Mulders 	macreg->m_margin = VLAN_TAGSZ;
3862ca5b659SJoost Mulders 
3872ca5b659SJoost Mulders 	if (mac_register(macreg, &vrp->machdl) != 0) {
3882ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "mac_register failed in attach");
3892ca5b659SJoost Mulders 		goto fail7;
3902ca5b659SJoost Mulders 	}
3912ca5b659SJoost Mulders 	mac_free(macreg);
3922ca5b659SJoost Mulders 	return (DDI_SUCCESS);
3932ca5b659SJoost Mulders 
3942ca5b659SJoost Mulders fail7:
3952ca5b659SJoost Mulders 	mac_free(macreg);
3962ca5b659SJoost Mulders fail6:
3972ca5b659SJoost Mulders 	(void) ddi_intr_disable(vrp->intr_hdl);
3982ca5b659SJoost Mulders fail5:
3992ca5b659SJoost Mulders 	mutex_destroy(&vrp->tx.lock);
4002ca5b659SJoost Mulders 	mutex_destroy(&vrp->oplock);
4012ca5b659SJoost Mulders 	mutex_destroy(&vrp->intrlock);
4022ca5b659SJoost Mulders 	vr_remove_intr(vrp);
4032ca5b659SJoost Mulders fail3:
4042ca5b659SJoost Mulders 	vr_remove_kstats(vrp);
4052ca5b659SJoost Mulders fail2:
4062ca5b659SJoost Mulders 	vr_rings_fini(vrp);
4072ca5b659SJoost Mulders fail1:
4082ca5b659SJoost Mulders 	vr_bus_unconfig(vrp);
4092ca5b659SJoost Mulders fail0:
4102ca5b659SJoost Mulders 	kmem_free(vrp, sizeof (vr_t));
4112ca5b659SJoost Mulders 	return (DDI_FAILURE);
4122ca5b659SJoost Mulders }
4132ca5b659SJoost Mulders 
4142ca5b659SJoost Mulders static int
vr_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)4152ca5b659SJoost Mulders vr_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
4162ca5b659SJoost Mulders {
4172ca5b659SJoost Mulders 	vr_t		*vrp;
4182ca5b659SJoost Mulders 
4192ca5b659SJoost Mulders 	vrp = ddi_get_driver_private(devinfo);
4202ca5b659SJoost Mulders 
4212ca5b659SJoost Mulders 	if (cmd == DDI_SUSPEND)
4222ca5b659SJoost Mulders 		return (vr_suspend(devinfo));
4232ca5b659SJoost Mulders 	else if (cmd != DDI_DETACH)
4242ca5b659SJoost Mulders 		return (DDI_FAILURE);
4252ca5b659SJoost Mulders 
4262ca5b659SJoost Mulders 	if (vrp->chip.state == CHIPSTATE_RUNNING)
4272ca5b659SJoost Mulders 		return (DDI_FAILURE);
4282ca5b659SJoost Mulders 
4292ca5b659SJoost Mulders 	/*
4302ca5b659SJoost Mulders 	 * Try to un-register from the MAC layer.
4312ca5b659SJoost Mulders 	 */
4322ca5b659SJoost Mulders 	if (mac_unregister(vrp->machdl) != 0)
4332ca5b659SJoost Mulders 		return (DDI_FAILURE);
4342ca5b659SJoost Mulders 
4352ca5b659SJoost Mulders 	(void) ddi_intr_disable(vrp->intr_hdl);
4362ca5b659SJoost Mulders 	vr_remove_intr(vrp);
4372ca5b659SJoost Mulders 	mutex_destroy(&vrp->tx.lock);
4382ca5b659SJoost Mulders 	mutex_destroy(&vrp->oplock);
4392ca5b659SJoost Mulders 	mutex_destroy(&vrp->intrlock);
4402ca5b659SJoost Mulders 	vr_remove_kstats(vrp);
4412ca5b659SJoost Mulders 	vr_rings_fini(vrp);
4422ca5b659SJoost Mulders 	vr_bus_unconfig(vrp);
4432ca5b659SJoost Mulders 	kmem_free(vrp, sizeof (vr_t));
4442ca5b659SJoost Mulders 	return (DDI_SUCCESS);
4452ca5b659SJoost Mulders }
4462ca5b659SJoost Mulders 
4472ca5b659SJoost Mulders /*
4482ca5b659SJoost Mulders  * quiesce the card for fast reboot.
4492ca5b659SJoost Mulders  */
4502ca5b659SJoost Mulders int
vr_quiesce(dev_info_t * dev_info)4512ca5b659SJoost Mulders vr_quiesce(dev_info_t *dev_info)
4522ca5b659SJoost Mulders {
4532ca5b659SJoost Mulders 	vr_t	*vrp;
4542ca5b659SJoost Mulders 
4552ca5b659SJoost Mulders 	vrp = (vr_t *)ddi_get_driver_private(dev_info);
4562ca5b659SJoost Mulders 
4572ca5b659SJoost Mulders 	/*
4582ca5b659SJoost Mulders 	 * Stop interrupts.
4592ca5b659SJoost Mulders 	 */
4602ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_ICR0, 0);
4612ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
4622ca5b659SJoost Mulders 
4632ca5b659SJoost Mulders 	/*
4642ca5b659SJoost Mulders 	 * Stop DMA.
4652ca5b659SJoost Mulders 	 */
4662ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_STOP);
4672ca5b659SJoost Mulders 	return (DDI_SUCCESS);
4682ca5b659SJoost Mulders }
4692ca5b659SJoost Mulders 
4702ca5b659SJoost Mulders /*
4712ca5b659SJoost Mulders  * Add an interrupt for our device to the OS.
4722ca5b659SJoost Mulders  */
4732ca5b659SJoost Mulders static vr_result_t
vr_add_intr(vr_t * vrp)4742ca5b659SJoost Mulders vr_add_intr(vr_t *vrp)
4752ca5b659SJoost Mulders {
4762ca5b659SJoost Mulders 	int	nintrs;
4772ca5b659SJoost Mulders 	int	rc;
4782ca5b659SJoost Mulders 
4792ca5b659SJoost Mulders 	rc = ddi_intr_alloc(vrp->devinfo, &vrp->intr_hdl,
4802ca5b659SJoost Mulders 	    DDI_INTR_TYPE_FIXED,	/* type */
4812ca5b659SJoost Mulders 	    0,			/* number */
4822ca5b659SJoost Mulders 	    1,			/* count */
4832ca5b659SJoost Mulders 	    &nintrs,		/* actualp */
4842ca5b659SJoost Mulders 	    DDI_INTR_ALLOC_STRICT);
4852ca5b659SJoost Mulders 
4862ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
4872ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_alloc failed: %d", rc);
4882ca5b659SJoost Mulders 		return (VR_FAILURE);
4892ca5b659SJoost Mulders 	}
4902ca5b659SJoost Mulders 
4912ca5b659SJoost Mulders 	rc = ddi_intr_add_handler(vrp->intr_hdl, vr_intr, vrp, NULL);
4922ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
4932ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_add_handler failed");
4942ca5b659SJoost Mulders 		if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
4952ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
4962ca5b659SJoost Mulders 		return (VR_FAILURE);
4972ca5b659SJoost Mulders 	}
4982ca5b659SJoost Mulders 
4992ca5b659SJoost Mulders 	rc = ddi_intr_get_pri(vrp->intr_hdl, &vrp->intr_pri);
5002ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
5012ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_get_pri failed");
5022ca5b659SJoost Mulders 		if (ddi_intr_remove_handler(vrp->intr_hdl) != DDI_SUCCESS)
5032ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "ddi_intr_remove_handler failed");
5042ca5b659SJoost Mulders 
5052ca5b659SJoost Mulders 		if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
5062ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
5072ca5b659SJoost Mulders 
5082ca5b659SJoost Mulders 		return (VR_FAILURE);
5092ca5b659SJoost Mulders 	}
5102ca5b659SJoost Mulders 	return (VR_SUCCESS);
5112ca5b659SJoost Mulders }
5122ca5b659SJoost Mulders 
5132ca5b659SJoost Mulders /*
5142ca5b659SJoost Mulders  * Remove our interrupt from the OS.
5152ca5b659SJoost Mulders  */
5162ca5b659SJoost Mulders static void
vr_remove_intr(vr_t * vrp)5172ca5b659SJoost Mulders vr_remove_intr(vr_t *vrp)
5182ca5b659SJoost Mulders {
5192ca5b659SJoost Mulders 	if (ddi_intr_remove_handler(vrp->intr_hdl) != DDI_SUCCESS)
5202ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_remove_handler failed");
5212ca5b659SJoost Mulders 
5222ca5b659SJoost Mulders 	if (ddi_intr_free(vrp->intr_hdl) != DDI_SUCCESS)
5232ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "ddi_intr_free failed");
5242ca5b659SJoost Mulders }
5252ca5b659SJoost Mulders 
5262ca5b659SJoost Mulders /*
5272ca5b659SJoost Mulders  * Resume operation after suspend.
5282ca5b659SJoost Mulders  */
5292ca5b659SJoost Mulders static int
vr_resume(dev_info_t * devinfo)5302ca5b659SJoost Mulders vr_resume(dev_info_t *devinfo)
5312ca5b659SJoost Mulders {
5322ca5b659SJoost Mulders 	vr_t *vrp;
5332ca5b659SJoost Mulders 
5342ca5b659SJoost Mulders 	vrp = (vr_t *)ddi_get_driver_private(devinfo);
5352ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
5362ca5b659SJoost Mulders 	if (vrp->chip.state == CHIPSTATE_SUSPENDED_RUNNING)
537c1374a13SSurya Prakki 		(void) vr_start(vrp);
5382ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
5392ca5b659SJoost Mulders 	return (DDI_SUCCESS);
5402ca5b659SJoost Mulders }
5412ca5b659SJoost Mulders 
5422ca5b659SJoost Mulders /*
5432ca5b659SJoost Mulders  * Suspend operation.
5442ca5b659SJoost Mulders  */
5452ca5b659SJoost Mulders static int
vr_suspend(dev_info_t * devinfo)5462ca5b659SJoost Mulders vr_suspend(dev_info_t *devinfo)
5472ca5b659SJoost Mulders {
5482ca5b659SJoost Mulders 	vr_t *vrp;
5492ca5b659SJoost Mulders 
5502ca5b659SJoost Mulders 	vrp = (vr_t *)ddi_get_driver_private(devinfo);
5512ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
5522ca5b659SJoost Mulders 	if (vrp->chip.state == CHIPSTATE_RUNNING) {
5532ca5b659SJoost Mulders 		(void) vr_stop(vrp);
5542ca5b659SJoost Mulders 		vrp->chip.state = CHIPSTATE_SUSPENDED_RUNNING;
5552ca5b659SJoost Mulders 	}
5562ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
5572ca5b659SJoost Mulders 	return (DDI_SUCCESS);
5582ca5b659SJoost Mulders }
5592ca5b659SJoost Mulders 
5602ca5b659SJoost Mulders /*
5612ca5b659SJoost Mulders  * Initial bus- and device configuration during attach(9E).
5622ca5b659SJoost Mulders  */
5632ca5b659SJoost Mulders static vr_result_t
vr_bus_config(vr_t * vrp)5642ca5b659SJoost Mulders vr_bus_config(vr_t *vrp)
5652ca5b659SJoost Mulders {
5662ca5b659SJoost Mulders 	uint32_t		addr;
5672ca5b659SJoost Mulders 	int			n, nsets, rc;
5682ca5b659SJoost Mulders 	uint_t			elem;
5692ca5b659SJoost Mulders 	pci_regspec_t		*regs;
5702ca5b659SJoost Mulders 
5712ca5b659SJoost Mulders 	/*
5722ca5b659SJoost Mulders 	 * Get the reg property which describes the various access methods.
5732ca5b659SJoost Mulders 	 */
5742ca5b659SJoost Mulders 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, vrp->devinfo,
5752ca5b659SJoost Mulders 	    0, "reg", (int **)&regs, &elem) != DDI_PROP_SUCCESS) {
5762ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "Can't get reg property");
5772ca5b659SJoost Mulders 		return (VR_FAILURE);
5782ca5b659SJoost Mulders 	}
5792ca5b659SJoost Mulders 	nsets = (elem * sizeof (uint_t)) / sizeof (pci_regspec_t);
5802ca5b659SJoost Mulders 
5812ca5b659SJoost Mulders 	/*
5822ca5b659SJoost Mulders 	 * Setup access to all available sets.
5832ca5b659SJoost Mulders 	 */
5842ca5b659SJoost Mulders 	vrp->nsets = nsets;
5852ca5b659SJoost Mulders 	vrp->regset = kmem_zalloc(nsets * sizeof (vr_acc_t), KM_SLEEP);
5862ca5b659SJoost Mulders 	for (n = 0; n < nsets; n++) {
5872ca5b659SJoost Mulders 		rc = ddi_regs_map_setup(vrp->devinfo, n,
5882ca5b659SJoost Mulders 		    &vrp->regset[n].addr, 0, 0,
5892ca5b659SJoost Mulders 		    &vr_dev_dma_accattr,
5902ca5b659SJoost Mulders 		    &vrp->regset[n].hdl);
5912ca5b659SJoost Mulders 		if (rc != DDI_SUCCESS) {
5922ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE,
5932ca5b659SJoost Mulders 			    "Setup of register set %d failed", n);
5942ca5b659SJoost Mulders 			while (--n >= 0)
5952ca5b659SJoost Mulders 				ddi_regs_map_free(&vrp->regset[n].hdl);
5962ca5b659SJoost Mulders 			kmem_free(vrp->regset, nsets * sizeof (vr_acc_t));
5972ca5b659SJoost Mulders 			ddi_prop_free(regs);
5982ca5b659SJoost Mulders 			return (VR_FAILURE);
5992ca5b659SJoost Mulders 		}
6002ca5b659SJoost Mulders 		bcopy(&regs[n], &vrp->regset[n].reg, sizeof (pci_regspec_t));
6012ca5b659SJoost Mulders 	}
6022ca5b659SJoost Mulders 	ddi_prop_free(regs);
6032ca5b659SJoost Mulders 
6042ca5b659SJoost Mulders 	/*
6052ca5b659SJoost Mulders 	 * Assign type-named pointers to the register sets.
6062ca5b659SJoost Mulders 	 */
6072ca5b659SJoost Mulders 	for (n = 0; n < nsets; n++) {
6082ca5b659SJoost Mulders 		addr = vrp->regset[n].reg.pci_phys_hi & PCI_REG_ADDR_M;
6092ca5b659SJoost Mulders 		if (addr == PCI_ADDR_CONFIG && vrp->acc_cfg == NULL)
6102ca5b659SJoost Mulders 			vrp->acc_cfg = &vrp->regset[n];
6112ca5b659SJoost Mulders 		else if (addr == PCI_ADDR_IO && vrp->acc_io == NULL)
6122ca5b659SJoost Mulders 			vrp->acc_io = &vrp->regset[n];
6132ca5b659SJoost Mulders 		else if (addr == PCI_ADDR_MEM32 && vrp->acc_mem == NULL)
6142ca5b659SJoost Mulders 			vrp->acc_mem = &vrp->regset[n];
6152ca5b659SJoost Mulders 	}
6162ca5b659SJoost Mulders 
6172ca5b659SJoost Mulders 	/*
6182ca5b659SJoost Mulders 	 * Assure there is one of each type.
6192ca5b659SJoost Mulders 	 */
6202ca5b659SJoost Mulders 	if (vrp->acc_cfg == NULL ||
6212ca5b659SJoost Mulders 	    vrp->acc_io == NULL ||
6222ca5b659SJoost Mulders 	    vrp->acc_mem == NULL) {
6232ca5b659SJoost Mulders 		for (n = 0; n < nsets; n++)
6242ca5b659SJoost Mulders 			ddi_regs_map_free(&vrp->regset[n].hdl);
6252ca5b659SJoost Mulders 		kmem_free(vrp->regset, nsets * sizeof (vr_acc_t));
6262ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
6272ca5b659SJoost Mulders 		    "Config-, I/O- and memory sets not available");
6282ca5b659SJoost Mulders 		return (VR_FAILURE);
6292ca5b659SJoost Mulders 	}
6302ca5b659SJoost Mulders 
6312ca5b659SJoost Mulders 	/*
6322ca5b659SJoost Mulders 	 * Store vendor/device/revision.
6332ca5b659SJoost Mulders 	 */
6342ca5b659SJoost Mulders 	vrp->chip.vendor = VR_GET16(vrp->acc_cfg, PCI_CONF_VENID);
6352ca5b659SJoost Mulders 	vrp->chip.device = VR_GET16(vrp->acc_cfg, PCI_CONF_DEVID);
6362ca5b659SJoost Mulders 	vrp->chip.revision = VR_GET16(vrp->acc_cfg, PCI_CONF_REVID);
6372ca5b659SJoost Mulders 
6382ca5b659SJoost Mulders 	/*
6392ca5b659SJoost Mulders 	 * Copy the matching chip_info_t structure.
6402ca5b659SJoost Mulders 	 */
6412ca5b659SJoost Mulders 	elem = sizeof (vr_chip_info) / sizeof (chip_info_t);
6422ca5b659SJoost Mulders 	for (n = 0; n < elem; n++) {
6432ca5b659SJoost Mulders 		if (vrp->chip.revision >= vr_chip_info[n].revmin &&
6442ca5b659SJoost Mulders 		    vrp->chip.revision <= vr_chip_info[n].revmax) {
6452ca5b659SJoost Mulders 			bcopy((void*)&vr_chip_info[n],
6462ca5b659SJoost Mulders 			    (void*)&vrp->chip.info,
6472ca5b659SJoost Mulders 			    sizeof (chip_info_t));
6482ca5b659SJoost Mulders 			break;
6492ca5b659SJoost Mulders 		}
6502ca5b659SJoost Mulders 	}
6512ca5b659SJoost Mulders 
6522ca5b659SJoost Mulders 	/*
6532ca5b659SJoost Mulders 	 * If we didn't find a chip_info_t for this card, copy the first
6542ca5b659SJoost Mulders 	 * entry of the info structures. This is a generic Rhine whith no
6552ca5b659SJoost Mulders 	 * bugs and no features.
6562ca5b659SJoost Mulders 	 */
657*cfe080a1SToomas Soome 	if (vrp->chip.info.name[0] == '\0') {
6582ca5b659SJoost Mulders 		bcopy((void*)&vr_chip_info[0],
6592ca5b659SJoost Mulders 		    (void*) &vrp->chip.info,
6602ca5b659SJoost Mulders 		    sizeof (chip_info_t));
6612ca5b659SJoost Mulders 	}
6622ca5b659SJoost Mulders 
6632ca5b659SJoost Mulders 	/*
6642ca5b659SJoost Mulders 	 * Tell what is found.
6652ca5b659SJoost Mulders 	 */
6662ca5b659SJoost Mulders 	vr_log(vrp, CE_NOTE, "pci%d,%d,%d: %s, revision 0x%0x",
6672ca5b659SJoost Mulders 	    PCI_REG_BUS_G(vrp->acc_cfg->reg.pci_phys_hi),
6682ca5b659SJoost Mulders 	    PCI_REG_DEV_G(vrp->acc_cfg->reg.pci_phys_hi),
6692ca5b659SJoost Mulders 	    PCI_REG_FUNC_G(vrp->acc_cfg->reg.pci_phys_hi),
6702ca5b659SJoost Mulders 	    vrp->chip.info.name,
6712ca5b659SJoost Mulders 	    vrp->chip.revision);
6722ca5b659SJoost Mulders 
6732ca5b659SJoost Mulders 	/*
6742ca5b659SJoost Mulders 	 * Assure that the device is prepared for memory space accesses
6752ca5b659SJoost Mulders 	 * This should be the default as the device advertises memory
6762ca5b659SJoost Mulders 	 * access in it's BAR's. However, my VT6102 on a EPIA CL board doesn't
6772ca5b659SJoost Mulders 	 * and thus we explicetely enable it.
6782ca5b659SJoost Mulders 	 */
6792ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_io, VR_CFGD, VR_CFGD_MMIOEN);
6802ca5b659SJoost Mulders 
6812ca5b659SJoost Mulders 	/*
6822ca5b659SJoost Mulders 	 * Setup a handle for regular usage, prefer memory space accesses.
6832ca5b659SJoost Mulders 	 */
6842ca5b659SJoost Mulders 	if (vrp->acc_mem != NULL &&
6852ca5b659SJoost Mulders 	    (vrp->chip.info.bugs & VR_BUG_NO_MEMIO) == 0)
6862ca5b659SJoost Mulders 		vrp->acc_reg = vrp->acc_mem;
6872ca5b659SJoost Mulders 	else
6882ca5b659SJoost Mulders 		vrp->acc_reg = vrp->acc_io;
6892ca5b659SJoost Mulders 
6902ca5b659SJoost Mulders 	/*
6912ca5b659SJoost Mulders 	 * Store the vendor's MAC address.
6922ca5b659SJoost Mulders 	 */
6932ca5b659SJoost Mulders 	for (n = 0; n < ETHERADDRL; n++) {
6942ca5b659SJoost Mulders 		vrp->vendor_ether_addr[n] = VR_GET8(vrp->acc_reg,
6952ca5b659SJoost Mulders 		    VR_ETHERADDR + n);
6962ca5b659SJoost Mulders 	}
6972ca5b659SJoost Mulders 	return (VR_SUCCESS);
6982ca5b659SJoost Mulders }
6992ca5b659SJoost Mulders 
7002ca5b659SJoost Mulders static void
vr_bus_unconfig(vr_t * vrp)7012ca5b659SJoost Mulders vr_bus_unconfig(vr_t *vrp)
7022ca5b659SJoost Mulders {
7032ca5b659SJoost Mulders 	uint_t	n;
7042ca5b659SJoost Mulders 
7052ca5b659SJoost Mulders 	/*
7062ca5b659SJoost Mulders 	 * Free the register access handles.
7072ca5b659SJoost Mulders 	 */
7082ca5b659SJoost Mulders 	for (n = 0; n < vrp->nsets; n++)
7092ca5b659SJoost Mulders 		ddi_regs_map_free(&vrp->regset[n].hdl);
7102ca5b659SJoost Mulders 	kmem_free(vrp->regset, vrp->nsets * sizeof (vr_acc_t));
7112ca5b659SJoost Mulders }
7122ca5b659SJoost Mulders 
7132ca5b659SJoost Mulders /*
7142ca5b659SJoost Mulders  * Initialize parameter structures.
7152ca5b659SJoost Mulders  */
7162ca5b659SJoost Mulders static void
vr_param_init(vr_t * vrp)7172ca5b659SJoost Mulders vr_param_init(vr_t *vrp)
7182ca5b659SJoost Mulders {
7192ca5b659SJoost Mulders 	/*
7202ca5b659SJoost Mulders 	 * Initialize default link configuration parameters.
7212ca5b659SJoost Mulders 	 */
7222ca5b659SJoost Mulders 	vrp->param.an_en = VR_LINK_AUTONEG_ON;
7232ca5b659SJoost Mulders 	vrp->param.anadv_en = 1; /* Select 802.3 autonegotiation */
7242ca5b659SJoost Mulders 	vrp->param.anadv_en |= MII_ABILITY_100BASE_T4;
7252ca5b659SJoost Mulders 	vrp->param.anadv_en |= MII_ABILITY_100BASE_TX_FD;
7262ca5b659SJoost Mulders 	vrp->param.anadv_en |= MII_ABILITY_100BASE_TX;
7272ca5b659SJoost Mulders 	vrp->param.anadv_en |= MII_ABILITY_10BASE_T_FD;
7282ca5b659SJoost Mulders 	vrp->param.anadv_en |= MII_ABILITY_10BASE_T;
7292ca5b659SJoost Mulders 	/* Not a PHY ability, but advertised on behalf of MAC */
730bdb9230aSGarrett D'Amore 	vrp->param.anadv_en |= MII_ABILITY_PAUSE;
7312ca5b659SJoost Mulders 	vrp->param.mtu = ETHERMTU;
7322ca5b659SJoost Mulders 
7332ca5b659SJoost Mulders 	/*
7342ca5b659SJoost Mulders 	 * Store the PHY identity.
7352ca5b659SJoost Mulders 	 */
7362ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_PHYIDH, &vrp->chip.mii.identh);
7372ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_PHYIDL, &vrp->chip.mii.identl);
7382ca5b659SJoost Mulders 
7392ca5b659SJoost Mulders 	/*
7402ca5b659SJoost Mulders 	 * Clear incapabilities imposed by PHY in phymask.
7412ca5b659SJoost Mulders 	 */
7422ca5b659SJoost Mulders 	vrp->param.an_phymask = vrp->param.anadv_en;
7432ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_STATUS, &vrp->chip.mii.status);
7442ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_10) == 0)
7452ca5b659SJoost Mulders 		vrp->param.an_phymask &= ~MII_ABILITY_10BASE_T;
7462ca5b659SJoost Mulders 
7472ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_10_FD) == 0)
7482ca5b659SJoost Mulders 		vrp->param.an_phymask &= ~MII_ABILITY_10BASE_T_FD;
7492ca5b659SJoost Mulders 
7502ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_100_BASEX) == 0)
7512ca5b659SJoost Mulders 		vrp->param.an_phymask &= ~MII_ABILITY_100BASE_TX;
7522ca5b659SJoost Mulders 
7532ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_100_BASEX_FD) == 0)
7542ca5b659SJoost Mulders 		vrp->param.an_phymask &= ~MII_ABILITY_100BASE_TX_FD;
7552ca5b659SJoost Mulders 
7562ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_100_BASE_T4) == 0)
7572ca5b659SJoost Mulders 		vrp->param.an_phymask &= ~MII_ABILITY_100BASE_T4;
7582ca5b659SJoost Mulders 
7592ca5b659SJoost Mulders 	/*
7602ca5b659SJoost Mulders 	 * Clear incapabilities imposed by MAC in macmask
7612ca5b659SJoost Mulders 	 * Note that flowcontrol (FCS?) is never masked. All of our adapters
7622ca5b659SJoost Mulders 	 * have the ability to honor incoming pause frames. Only the newer can
7632ca5b659SJoost Mulders 	 * transmit pause frames. Since there's no asym flowcontrol in 100Mbit
7642ca5b659SJoost Mulders 	 * Ethernet, we always advertise (symmetric) pause.
7652ca5b659SJoost Mulders 	 */
7662ca5b659SJoost Mulders 	vrp->param.an_macmask = vrp->param.anadv_en;
7672ca5b659SJoost Mulders 
7682ca5b659SJoost Mulders 	/*
7692ca5b659SJoost Mulders 	 * Advertised capabilities is enabled minus incapable.
7702ca5b659SJoost Mulders 	 */
7712ca5b659SJoost Mulders 	vrp->chip.mii.anadv = vrp->param.anadv_en &
7722ca5b659SJoost Mulders 	    (vrp->param.an_phymask & vrp->param.an_macmask);
7732ca5b659SJoost Mulders 
7742ca5b659SJoost Mulders 	/*
7752ca5b659SJoost Mulders 	 * Ensure that autoneg of the PHY matches our default.
7762ca5b659SJoost Mulders 	 */
7772ca5b659SJoost Mulders 	if (vrp->param.an_en == VR_LINK_AUTONEG_ON)
7782ca5b659SJoost Mulders 		vrp->chip.mii.control = MII_CONTROL_ANE;
7792ca5b659SJoost Mulders 	else
7802ca5b659SJoost Mulders 		vrp->chip.mii.control =
7812ca5b659SJoost Mulders 		    (MII_CONTROL_100MB | MII_CONTROL_FDUPLEX);
7822ca5b659SJoost Mulders }
7832ca5b659SJoost Mulders 
7842ca5b659SJoost Mulders /*
7852ca5b659SJoost Mulders  * Setup the descriptor rings.
7862ca5b659SJoost Mulders  */
7872ca5b659SJoost Mulders static vr_result_t
vr_rings_init(vr_t * vrp)7882ca5b659SJoost Mulders vr_rings_init(vr_t *vrp)
7892ca5b659SJoost Mulders {
7902ca5b659SJoost Mulders 
7912ca5b659SJoost Mulders 	vrp->rx.ndesc = VR_RX_N_DESC;
7922ca5b659SJoost Mulders 	vrp->tx.ndesc = VR_TX_N_DESC;
7932ca5b659SJoost Mulders 
7942ca5b659SJoost Mulders 	/*
7952ca5b659SJoost Mulders 	 * Create a ring for receive.
7962ca5b659SJoost Mulders 	 */
7972ca5b659SJoost Mulders 	if (vr_alloc_ring(vrp, &vrp->rxring, vrp->rx.ndesc) != VR_SUCCESS)
7982ca5b659SJoost Mulders 		return (VR_FAILURE);
7992ca5b659SJoost Mulders 
8002ca5b659SJoost Mulders 	/*
8012ca5b659SJoost Mulders 	 * Create a ring for transmit.
8022ca5b659SJoost Mulders 	 */
8032ca5b659SJoost Mulders 	if (vr_alloc_ring(vrp, &vrp->txring, vrp->tx.ndesc) != VR_SUCCESS) {
8042ca5b659SJoost Mulders 		vr_free_ring(&vrp->rxring, vrp->rx.ndesc);
8052ca5b659SJoost Mulders 		return (VR_FAILURE);
8062ca5b659SJoost Mulders 	}
8072ca5b659SJoost Mulders 
8082ca5b659SJoost Mulders 	vrp->rx.ring = vrp->rxring.desc;
8092ca5b659SJoost Mulders 	vrp->tx.ring = vrp->txring.desc;
8102ca5b659SJoost Mulders 	return (VR_SUCCESS);
8112ca5b659SJoost Mulders }
8122ca5b659SJoost Mulders 
8132ca5b659SJoost Mulders static void
vr_rings_fini(vr_t * vrp)8142ca5b659SJoost Mulders vr_rings_fini(vr_t *vrp)
8152ca5b659SJoost Mulders {
8162ca5b659SJoost Mulders 	vr_free_ring(&vrp->rxring, vrp->rx.ndesc);
8172ca5b659SJoost Mulders 	vr_free_ring(&vrp->txring, vrp->tx.ndesc);
8182ca5b659SJoost Mulders }
8192ca5b659SJoost Mulders 
8202ca5b659SJoost Mulders /*
8212ca5b659SJoost Mulders  * Allocate a descriptor ring
8222ca5b659SJoost Mulders  * The number of descriptor entries must fit in a single page so that the
8232ca5b659SJoost Mulders  * whole ring fits in one consequtive space.
8242ca5b659SJoost Mulders  *  i386:  4K page / 16 byte descriptor = 256 entries
8252ca5b659SJoost Mulders  *  sparc: 8K page / 16 byte descriptor = 512 entries
8262ca5b659SJoost Mulders  */
8272ca5b659SJoost Mulders static vr_result_t
vr_alloc_ring(vr_t * vrp,vr_ring_t * ring,size_t n)8282ca5b659SJoost Mulders vr_alloc_ring(vr_t *vrp, vr_ring_t *ring, size_t n)
8292ca5b659SJoost Mulders {
8302ca5b659SJoost Mulders 	ddi_dma_cookie_t	desc_dma_cookie;
8312ca5b659SJoost Mulders 	uint_t			desc_cookiecnt;
8322ca5b659SJoost Mulders 	int			i, rc;
8332ca5b659SJoost Mulders 	size_t			rbytes;
8342ca5b659SJoost Mulders 
8352ca5b659SJoost Mulders 	/*
8362ca5b659SJoost Mulders 	 * Allocate a DMA handle for the chip descriptors.
8372ca5b659SJoost Mulders 	 */
8382ca5b659SJoost Mulders 	rc = ddi_dma_alloc_handle(vrp->devinfo,
8392ca5b659SJoost Mulders 	    &vr_dev_dma_attr,
8402ca5b659SJoost Mulders 	    DDI_DMA_SLEEP,
8412ca5b659SJoost Mulders 	    NULL,
8422ca5b659SJoost Mulders 	    &ring->handle);
8432ca5b659SJoost Mulders 
8442ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
8452ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
8462ca5b659SJoost Mulders 		    "ddi_dma_alloc_handle in vr_alloc_ring failed.");
8472ca5b659SJoost Mulders 		return (VR_FAILURE);
8482ca5b659SJoost Mulders 	}
8492ca5b659SJoost Mulders 
8502ca5b659SJoost Mulders 	/*
8512ca5b659SJoost Mulders 	 * Allocate memory for the chip descriptors.
8522ca5b659SJoost Mulders 	 */
8532ca5b659SJoost Mulders 	rc = ddi_dma_mem_alloc(ring->handle,
8542ca5b659SJoost Mulders 	    n * sizeof (vr_chip_desc_t),
8552ca5b659SJoost Mulders 	    &vr_dev_dma_accattr,
8562ca5b659SJoost Mulders 	    DDI_DMA_CONSISTENT,
8572ca5b659SJoost Mulders 	    DDI_DMA_SLEEP,
8582ca5b659SJoost Mulders 	    NULL,
8592ca5b659SJoost Mulders 	    (caddr_t *)&ring->cdesc,
8602ca5b659SJoost Mulders 	    &rbytes,
8612ca5b659SJoost Mulders 	    &ring->acchdl);
8622ca5b659SJoost Mulders 
8632ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
8642ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
8652ca5b659SJoost Mulders 		    "ddi_dma_mem_alloc in vr_alloc_ring failed.");
8662ca5b659SJoost Mulders 		ddi_dma_free_handle(&ring->handle);
8672ca5b659SJoost Mulders 		return (VR_FAILURE);
8682ca5b659SJoost Mulders 	}
8692ca5b659SJoost Mulders 
8702ca5b659SJoost Mulders 	/*
8712ca5b659SJoost Mulders 	 * Map the descriptor memory.
8722ca5b659SJoost Mulders 	 */
8732ca5b659SJoost Mulders 	rc = ddi_dma_addr_bind_handle(ring->handle,
8742ca5b659SJoost Mulders 	    NULL,
8752ca5b659SJoost Mulders 	    (caddr_t)ring->cdesc,
8762ca5b659SJoost Mulders 	    rbytes,
8772ca5b659SJoost Mulders 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
8782ca5b659SJoost Mulders 	    DDI_DMA_SLEEP,
8792ca5b659SJoost Mulders 	    NULL,
8802ca5b659SJoost Mulders 	    &desc_dma_cookie,
8812ca5b659SJoost Mulders 	    &desc_cookiecnt);
8822ca5b659SJoost Mulders 
8832ca5b659SJoost Mulders 	if (rc != DDI_DMA_MAPPED || desc_cookiecnt > 1) {
8842ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
8852ca5b659SJoost Mulders 		    "ddi_dma_addr_bind_handle in vr_alloc_ring failed: "
8862ca5b659SJoost Mulders 		    "rc = %d, cookiecnt = %d", rc, desc_cookiecnt);
8872ca5b659SJoost Mulders 		ddi_dma_mem_free(&ring->acchdl);
8882ca5b659SJoost Mulders 		ddi_dma_free_handle(&ring->handle);
8892ca5b659SJoost Mulders 		return (VR_FAILURE);
8902ca5b659SJoost Mulders 	}
8912ca5b659SJoost Mulders 	ring->cdesc_paddr = desc_dma_cookie.dmac_address;
8922ca5b659SJoost Mulders 
8932ca5b659SJoost Mulders 	/*
8942ca5b659SJoost Mulders 	 * Allocate memory for the host descriptor ring.
8952ca5b659SJoost Mulders 	 */
8962ca5b659SJoost Mulders 	ring->desc =
8972ca5b659SJoost Mulders 	    (vr_desc_t *)kmem_zalloc(n * sizeof (vr_desc_t), KM_SLEEP);
8982ca5b659SJoost Mulders 
8992ca5b659SJoost Mulders 	/*
9002ca5b659SJoost Mulders 	 * Interlink the descriptors and connect host- to chip descriptors.
9012ca5b659SJoost Mulders 	 */
9022ca5b659SJoost Mulders 	for (i = 0; i < n; i++) {
9032ca5b659SJoost Mulders 		/*
9042ca5b659SJoost Mulders 		 * Connect the host descriptor to a chip descriptor.
9052ca5b659SJoost Mulders 		 */
9062ca5b659SJoost Mulders 		ring->desc[i].cdesc = &ring->cdesc[i];
9072ca5b659SJoost Mulders 
9082ca5b659SJoost Mulders 		/*
9092ca5b659SJoost Mulders 		 * Store the DMA address and offset in the descriptor
9102ca5b659SJoost Mulders 		 * Offset is for ddi_dma_sync() and paddr is for ddi_get/-put().
9112ca5b659SJoost Mulders 		 */
9122ca5b659SJoost Mulders 		ring->desc[i].offset = i * sizeof (vr_chip_desc_t);
9132ca5b659SJoost Mulders 		ring->desc[i].paddr = ring->cdesc_paddr + ring->desc[i].offset;
9142ca5b659SJoost Mulders 
9152ca5b659SJoost Mulders 		/*
9162ca5b659SJoost Mulders 		 * Link the previous descriptor to this one.
9172ca5b659SJoost Mulders 		 */
9182ca5b659SJoost Mulders 		if (i > 0) {
9192ca5b659SJoost Mulders 			/* Host */
9202ca5b659SJoost Mulders 			ring->desc[i-1].next = &ring->desc[i];
9212ca5b659SJoost Mulders 
9222ca5b659SJoost Mulders 			/* Chip */
9232ca5b659SJoost Mulders 			ddi_put32(ring->acchdl,
9242ca5b659SJoost Mulders 			    &ring->cdesc[i-1].next,
9252ca5b659SJoost Mulders 			    ring->desc[i].paddr);
9262ca5b659SJoost Mulders 		}
9272ca5b659SJoost Mulders 	}
9282ca5b659SJoost Mulders 
9292ca5b659SJoost Mulders 	/*
9302ca5b659SJoost Mulders 	 * Make rings out of this list by pointing last to first.
9312ca5b659SJoost Mulders 	 */
9322ca5b659SJoost Mulders 	i = n - 1;
9332ca5b659SJoost Mulders 	ring->desc[i].next = &ring->desc[0];
9342ca5b659SJoost Mulders 	ddi_put32(ring->acchdl, &ring->cdesc[i].next, ring->desc[0].paddr);
9352ca5b659SJoost Mulders 	return (VR_SUCCESS);
9362ca5b659SJoost Mulders }
9372ca5b659SJoost Mulders 
9382ca5b659SJoost Mulders /*
9392ca5b659SJoost Mulders  * Free the memory allocated for a ring.
9402ca5b659SJoost Mulders  */
9412ca5b659SJoost Mulders static void
vr_free_ring(vr_ring_t * r,size_t n)9422ca5b659SJoost Mulders vr_free_ring(vr_ring_t *r, size_t n)
9432ca5b659SJoost Mulders {
9442ca5b659SJoost Mulders 	/*
9452ca5b659SJoost Mulders 	 * Unmap and free the chip descriptors.
9462ca5b659SJoost Mulders 	 */
9472ca5b659SJoost Mulders 	(void) ddi_dma_unbind_handle(r->handle);
9482ca5b659SJoost Mulders 	ddi_dma_mem_free(&r->acchdl);
9492ca5b659SJoost Mulders 	ddi_dma_free_handle(&r->handle);
9502ca5b659SJoost Mulders 
9512ca5b659SJoost Mulders 	/*
9522ca5b659SJoost Mulders 	 * Free the memory for storing host descriptors
9532ca5b659SJoost Mulders 	 */
9542ca5b659SJoost Mulders 	kmem_free(r->desc, n * sizeof (vr_desc_t));
9552ca5b659SJoost Mulders }
9562ca5b659SJoost Mulders 
9572ca5b659SJoost Mulders /*
9582ca5b659SJoost Mulders  * Initialize the receive ring.
9592ca5b659SJoost Mulders  */
9602ca5b659SJoost Mulders static vr_result_t
vr_rxring_init(vr_t * vrp)9612ca5b659SJoost Mulders vr_rxring_init(vr_t *vrp)
9622ca5b659SJoost Mulders {
9632ca5b659SJoost Mulders 	int		i, rc;
9642ca5b659SJoost Mulders 	vr_desc_t	*rp;
9652ca5b659SJoost Mulders 
9662ca5b659SJoost Mulders 	/*
9672ca5b659SJoost Mulders 	 * Set the read pointer at the start of the ring.
9682ca5b659SJoost Mulders 	 */
9692ca5b659SJoost Mulders 	vrp->rx.rp = &vrp->rx.ring[0];
9702ca5b659SJoost Mulders 
9712ca5b659SJoost Mulders 	/*
9722ca5b659SJoost Mulders 	 * Assign a DMA buffer to each receive descriptor.
9732ca5b659SJoost Mulders 	 */
9742ca5b659SJoost Mulders 	for (i = 0; i < vrp->rx.ndesc; i++) {
9752ca5b659SJoost Mulders 		rp = &vrp->rx.ring[i];
9762ca5b659SJoost Mulders 		rc = vr_alloc_dmabuf(vrp,
9772ca5b659SJoost Mulders 		    &vrp->rx.ring[i].dmabuf,
9782ca5b659SJoost Mulders 		    DDI_DMA_STREAMING | DDI_DMA_READ);
9792ca5b659SJoost Mulders 
9802ca5b659SJoost Mulders 		if (rc != VR_SUCCESS) {
9812ca5b659SJoost Mulders 			while (--i >= 0)
9822ca5b659SJoost Mulders 				vr_free_dmabuf(&vrp->rx.ring[i].dmabuf);
9832ca5b659SJoost Mulders 			return (VR_FAILURE);
9842ca5b659SJoost Mulders 		}
9852ca5b659SJoost Mulders 
9862ca5b659SJoost Mulders 		/*
9872ca5b659SJoost Mulders 		 * Store the address of the dma buffer in the chip descriptor
9882ca5b659SJoost Mulders 		 */
9892ca5b659SJoost Mulders 		ddi_put32(vrp->rxring.acchdl,
9902ca5b659SJoost Mulders 		    &rp->cdesc->data,
9912ca5b659SJoost Mulders 		    rp->dmabuf.paddr);
9922ca5b659SJoost Mulders 
9932ca5b659SJoost Mulders 		/*
9942ca5b659SJoost Mulders 		 * Put the buffer length in the chip descriptor. Ensure that
9952ca5b659SJoost Mulders 		 * length fits in the 11 bits of stat1 (2047/0x7FF)
9962ca5b659SJoost Mulders 		 */
9972ca5b659SJoost Mulders 		ddi_put32(vrp->rxring.acchdl, &rp->cdesc->stat1,
9982ca5b659SJoost Mulders 		    MIN(VR_MAX_PKTSZ, rp->dmabuf.bufsz));
9992ca5b659SJoost Mulders 
10002ca5b659SJoost Mulders 		/*
10012ca5b659SJoost Mulders 		 * Set descriptor ownership to the card
10022ca5b659SJoost Mulders 		 */
10032ca5b659SJoost Mulders 		ddi_put32(vrp->rxring.acchdl, &rp->cdesc->stat0, VR_RDES0_OWN);
10042ca5b659SJoost Mulders 
10052ca5b659SJoost Mulders 		/*
10062ca5b659SJoost Mulders 		 * Sync the descriptor with main memory
10072ca5b659SJoost Mulders 		 */
10082ca5b659SJoost Mulders 		(void) ddi_dma_sync(vrp->rxring.handle, rp->offset,
10092ca5b659SJoost Mulders 		    sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
10102ca5b659SJoost Mulders 	}
10112ca5b659SJoost Mulders 	return (VR_SUCCESS);
10122ca5b659SJoost Mulders }
10132ca5b659SJoost Mulders 
10142ca5b659SJoost Mulders /*
10152ca5b659SJoost Mulders  * Free the DMA buffers assigned to the receive ring.
10162ca5b659SJoost Mulders  */
10172ca5b659SJoost Mulders static void
vr_rxring_fini(vr_t * vrp)10182ca5b659SJoost Mulders vr_rxring_fini(vr_t *vrp)
10192ca5b659SJoost Mulders {
10202ca5b659SJoost Mulders 	int		i;
10212ca5b659SJoost Mulders 
10222ca5b659SJoost Mulders 	for (i = 0; i < vrp->rx.ndesc; i++)
10232ca5b659SJoost Mulders 		vr_free_dmabuf(&vrp->rx.ring[i].dmabuf);
10242ca5b659SJoost Mulders }
10252ca5b659SJoost Mulders 
10262ca5b659SJoost Mulders static vr_result_t
vr_txring_init(vr_t * vrp)10272ca5b659SJoost Mulders vr_txring_init(vr_t *vrp)
10282ca5b659SJoost Mulders {
10292ca5b659SJoost Mulders 	vr_desc_t		*wp;
10302ca5b659SJoost Mulders 	int			i, rc;
10312ca5b659SJoost Mulders 
10322ca5b659SJoost Mulders 	/*
10332ca5b659SJoost Mulders 	 * Set the write- and claim pointer.
10342ca5b659SJoost Mulders 	 */
10352ca5b659SJoost Mulders 	vrp->tx.wp = &vrp->tx.ring[0];
10362ca5b659SJoost Mulders 	vrp->tx.cp = &vrp->tx.ring[0];
10372ca5b659SJoost Mulders 
10382ca5b659SJoost Mulders 	/*
10392ca5b659SJoost Mulders 	 * (Re)set the TX bookkeeping.
10402ca5b659SJoost Mulders 	 */
10412ca5b659SJoost Mulders 	vrp->tx.stallticks = 0;
10422ca5b659SJoost Mulders 	vrp->tx.resched = 0;
10432ca5b659SJoost Mulders 
10442ca5b659SJoost Mulders 	/*
10452ca5b659SJoost Mulders 	 * Every transmit decreases nfree. Every reclaim increases nfree.
10462ca5b659SJoost Mulders 	 */
10472ca5b659SJoost Mulders 	vrp->tx.nfree = vrp->tx.ndesc;
10482ca5b659SJoost Mulders 
10492ca5b659SJoost Mulders 	/*
10502ca5b659SJoost Mulders 	 * Attach a DMA buffer to each transmit descriptor.
10512ca5b659SJoost Mulders 	 */
10522ca5b659SJoost Mulders 	for (i = 0; i < vrp->tx.ndesc; i++) {
10532ca5b659SJoost Mulders 		rc = vr_alloc_dmabuf(vrp,
10542ca5b659SJoost Mulders 		    &vrp->tx.ring[i].dmabuf,
10552ca5b659SJoost Mulders 		    DDI_DMA_STREAMING | DDI_DMA_WRITE);
10562ca5b659SJoost Mulders 
10572ca5b659SJoost Mulders 		if (rc != VR_SUCCESS) {
10582ca5b659SJoost Mulders 			while (--i >= 0)
10592ca5b659SJoost Mulders 				vr_free_dmabuf(&vrp->tx.ring[i].dmabuf);
10602ca5b659SJoost Mulders 			return (VR_FAILURE);
10612ca5b659SJoost Mulders 		}
10622ca5b659SJoost Mulders 	}
10632ca5b659SJoost Mulders 
10642ca5b659SJoost Mulders 	/*
10652ca5b659SJoost Mulders 	 * Init & sync the TX descriptors so the device sees a valid ring.
10662ca5b659SJoost Mulders 	 */
10672ca5b659SJoost Mulders 	for (i = 0; i < vrp->tx.ndesc; i++) {
10682ca5b659SJoost Mulders 		wp = &vrp->tx.ring[i];
10692ca5b659SJoost Mulders 		ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat0, 0);
10702ca5b659SJoost Mulders 		ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat1, 0);
10712ca5b659SJoost Mulders 		ddi_put32(vrp->txring.acchdl, &wp->cdesc->data,
10722ca5b659SJoost Mulders 		    wp->dmabuf.paddr);
10732ca5b659SJoost Mulders 		(void) ddi_dma_sync(vrp->txring.handle, wp->offset,
10742ca5b659SJoost Mulders 		    sizeof (vr_chip_desc_t),
10752ca5b659SJoost Mulders 		    DDI_DMA_SYNC_FORDEV);
10762ca5b659SJoost Mulders 	}
10772ca5b659SJoost Mulders 	return (VR_SUCCESS);
10782ca5b659SJoost Mulders }
10792ca5b659SJoost Mulders 
10802ca5b659SJoost Mulders /*
10812ca5b659SJoost Mulders  * Free the DMA buffers attached to the TX ring.
10822ca5b659SJoost Mulders  */
10832ca5b659SJoost Mulders static void
vr_txring_fini(vr_t * vrp)10842ca5b659SJoost Mulders vr_txring_fini(vr_t *vrp)
10852ca5b659SJoost Mulders {
10862ca5b659SJoost Mulders 	int		i;
10872ca5b659SJoost Mulders 
10882ca5b659SJoost Mulders 	/*
10892ca5b659SJoost Mulders 	 * Free the DMA buffers attached to the TX ring
10902ca5b659SJoost Mulders 	 */
10912ca5b659SJoost Mulders 	for (i = 0; i < vrp->tx.ndesc; i++)
10922ca5b659SJoost Mulders 		vr_free_dmabuf(&vrp->tx.ring[i].dmabuf);
10932ca5b659SJoost Mulders }
10942ca5b659SJoost Mulders 
10952ca5b659SJoost Mulders /*
10962ca5b659SJoost Mulders  * Allocate a DMA buffer.
10972ca5b659SJoost Mulders  */
10982ca5b659SJoost Mulders static vr_result_t
vr_alloc_dmabuf(vr_t * vrp,vr_data_dma_t * dmap,uint_t dmaflags)10992ca5b659SJoost Mulders vr_alloc_dmabuf(vr_t *vrp, vr_data_dma_t *dmap, uint_t dmaflags)
11002ca5b659SJoost Mulders {
11012ca5b659SJoost Mulders 	ddi_dma_cookie_t	dma_cookie;
11022ca5b659SJoost Mulders 	uint_t			cookiecnt;
11032ca5b659SJoost Mulders 	int			rc;
11042ca5b659SJoost Mulders 
11052ca5b659SJoost Mulders 	/*
11062ca5b659SJoost Mulders 	 * Allocate a DMA handle for the buffer
11072ca5b659SJoost Mulders 	 */
11082ca5b659SJoost Mulders 	rc = ddi_dma_alloc_handle(vrp->devinfo,
11092ca5b659SJoost Mulders 	    &vr_data_dma_attr,
11102ca5b659SJoost Mulders 	    DDI_DMA_DONTWAIT, NULL,
11112ca5b659SJoost Mulders 	    &dmap->handle);
11122ca5b659SJoost Mulders 
11132ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
11142ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
11152ca5b659SJoost Mulders 		    "ddi_dma_alloc_handle failed in vr_alloc_dmabuf");
11162ca5b659SJoost Mulders 		return (VR_FAILURE);
11172ca5b659SJoost Mulders 	}
11182ca5b659SJoost Mulders 
11192ca5b659SJoost Mulders 	/*
11202ca5b659SJoost Mulders 	 * Allocate the buffer
11212ca5b659SJoost Mulders 	 * The allocated buffer is aligned on 2K boundary. This ensures that
11222ca5b659SJoost Mulders 	 * a 1500 byte frame never cross a page boundary and thus that the DMA
11232ca5b659SJoost Mulders 	 * mapping can be established in 1 fragment.
11242ca5b659SJoost Mulders 	 */
11252ca5b659SJoost Mulders 	rc = ddi_dma_mem_alloc(dmap->handle,
11262ca5b659SJoost Mulders 	    VR_DMABUFSZ,
11272ca5b659SJoost Mulders 	    &vr_data_dma_accattr,
11282ca5b659SJoost Mulders 	    DDI_DMA_RDWR | DDI_DMA_STREAMING,
11292ca5b659SJoost Mulders 	    DDI_DMA_DONTWAIT, NULL,
11302ca5b659SJoost Mulders 	    &dmap->buf,
11312ca5b659SJoost Mulders 	    &dmap->bufsz,
11322ca5b659SJoost Mulders 	    &dmap->acchdl);
11332ca5b659SJoost Mulders 
11342ca5b659SJoost Mulders 	if (rc != DDI_SUCCESS) {
11352ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
11362ca5b659SJoost Mulders 		    "ddi_dma_mem_alloc failed in vr_alloc_dmabuf");
11372ca5b659SJoost Mulders 		ddi_dma_free_handle(&dmap->handle);
11382ca5b659SJoost Mulders 		return (VR_FAILURE);
11392ca5b659SJoost Mulders 	}
11402ca5b659SJoost Mulders 
11412ca5b659SJoost Mulders 	/*
11422ca5b659SJoost Mulders 	 * Map the memory
11432ca5b659SJoost Mulders 	 */
11442ca5b659SJoost Mulders 	rc = ddi_dma_addr_bind_handle(dmap->handle,
11452ca5b659SJoost Mulders 	    NULL,
11462ca5b659SJoost Mulders 	    (caddr_t)dmap->buf,
11472ca5b659SJoost Mulders 	    dmap->bufsz,
11482ca5b659SJoost Mulders 	    dmaflags,
11492ca5b659SJoost Mulders 	    DDI_DMA_DONTWAIT,
11502ca5b659SJoost Mulders 	    NULL,
11512ca5b659SJoost Mulders 	    &dma_cookie,
11522ca5b659SJoost Mulders 	    &cookiecnt);
11532ca5b659SJoost Mulders 
11542ca5b659SJoost Mulders 	/*
11552ca5b659SJoost Mulders 	 * The cookiecount should never > 1 because we requested 2K alignment
11562ca5b659SJoost Mulders 	 */
11572ca5b659SJoost Mulders 	if (rc != DDI_DMA_MAPPED || cookiecnt > 1) {
11582ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN,
11592ca5b659SJoost Mulders 		    "dma_addr_bind_handle failed in vr_alloc_dmabuf: "
11602ca5b659SJoost Mulders 		    "rc = %d, cookiecnt = %d", rc, cookiecnt);
11612ca5b659SJoost Mulders 		ddi_dma_mem_free(&dmap->acchdl);
11622ca5b659SJoost Mulders 		ddi_dma_free_handle(&dmap->handle);
11632ca5b659SJoost Mulders 		return (VR_FAILURE);
11642ca5b659SJoost Mulders 	}
11652ca5b659SJoost Mulders 	dmap->paddr = dma_cookie.dmac_address;
11662ca5b659SJoost Mulders 	return (VR_SUCCESS);
11672ca5b659SJoost Mulders }
11682ca5b659SJoost Mulders 
11692ca5b659SJoost Mulders /*
11702ca5b659SJoost Mulders  * Destroy a DMA buffer.
11712ca5b659SJoost Mulders  */
11722ca5b659SJoost Mulders static void
vr_free_dmabuf(vr_data_dma_t * dmap)11732ca5b659SJoost Mulders vr_free_dmabuf(vr_data_dma_t *dmap)
11742ca5b659SJoost Mulders {
11752ca5b659SJoost Mulders 	(void) ddi_dma_unbind_handle(dmap->handle);
11762ca5b659SJoost Mulders 	ddi_dma_mem_free(&dmap->acchdl);
11772ca5b659SJoost Mulders 	ddi_dma_free_handle(&dmap->handle);
11782ca5b659SJoost Mulders }
11792ca5b659SJoost Mulders 
11802ca5b659SJoost Mulders /*
11812ca5b659SJoost Mulders  * Interrupt service routine
11822ca5b659SJoost Mulders  * When our vector is shared with another device, av_dispatch_autovect calls
11832ca5b659SJoost Mulders  * all service routines for the vector until *none* of them return claimed
11842ca5b659SJoost Mulders  * That means that, when sharing vectors, this routine is called at least
11852ca5b659SJoost Mulders  * twice for each interrupt.
11862ca5b659SJoost Mulders  */
11872ca5b659SJoost Mulders uint_t
vr_intr(caddr_t arg1,caddr_t arg2)11882ca5b659SJoost Mulders vr_intr(caddr_t arg1, caddr_t arg2)
11892ca5b659SJoost Mulders {
11902ca5b659SJoost Mulders 	vr_t		*vrp;
11912ca5b659SJoost Mulders 	uint16_t	status;
11922ca5b659SJoost Mulders 	mblk_t		*lp = NULL;
11932ca5b659SJoost Mulders 	uint32_t	tx_resched;
11942ca5b659SJoost Mulders 	uint32_t	link_change;
11952ca5b659SJoost Mulders 
11962ca5b659SJoost Mulders 	tx_resched = 0;
11972ca5b659SJoost Mulders 	link_change = 0;
11982ca5b659SJoost Mulders 	vrp = (void *)arg1;
11992ca5b659SJoost Mulders 	_NOTE(ARGUNUSED(arg2))
12002ca5b659SJoost Mulders 
12015815e35bSjoostmnl@gmail.com 	mutex_enter(&vrp->intrlock);
12025815e35bSjoostmnl@gmail.com 	/*
12035815e35bSjoostmnl@gmail.com 	 * If the driver is not in running state it is not our interrupt.
12045815e35bSjoostmnl@gmail.com 	 * Shared interrupts can end up here without us being started.
12055815e35bSjoostmnl@gmail.com 	 */
12065815e35bSjoostmnl@gmail.com 	if (vrp->chip.state != CHIPSTATE_RUNNING) {
12075815e35bSjoostmnl@gmail.com 		mutex_exit(&vrp->intrlock);
12085815e35bSjoostmnl@gmail.com 		return (DDI_INTR_UNCLAIMED);
12095815e35bSjoostmnl@gmail.com 	}
12105815e35bSjoostmnl@gmail.com 
12112ca5b659SJoost Mulders 	/*
12122ca5b659SJoost Mulders 	 * Read the status register to see if the interrupt is from our device
12132ca5b659SJoost Mulders 	 * This read also ensures that posted writes are brought to main memory.
12142ca5b659SJoost Mulders 	 */
12152ca5b659SJoost Mulders 	status = VR_GET16(vrp->acc_reg, VR_ISR0) & VR_ICR0_CFG;
12162ca5b659SJoost Mulders 	if (status == 0) {
12172ca5b659SJoost Mulders 		/*
12182ca5b659SJoost Mulders 		 * Status contains no configured interrupts
12192ca5b659SJoost Mulders 		 * The interrupt was not generated by our device.
12202ca5b659SJoost Mulders 		 */
12212ca5b659SJoost Mulders 		vrp->stats.intr_unclaimed++;
12222ca5b659SJoost Mulders 		mutex_exit(&vrp->intrlock);
12232ca5b659SJoost Mulders 		return (DDI_INTR_UNCLAIMED);
12242ca5b659SJoost Mulders 	}
12252ca5b659SJoost Mulders 	vrp->stats.intr_claimed++;
12262ca5b659SJoost Mulders 
12272ca5b659SJoost Mulders 	/*
12282ca5b659SJoost Mulders 	 * Acknowledge the event(s) that caused interruption.
12292ca5b659SJoost Mulders 	 */
12302ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_ISR0, status);
12312ca5b659SJoost Mulders 
12322ca5b659SJoost Mulders 	/*
12332ca5b659SJoost Mulders 	 * Receive completion.
12342ca5b659SJoost Mulders 	 */
12352ca5b659SJoost Mulders 	if ((status & (VR_ISR0_RX_DONE | VR_ISR_RX_ERR_BITS)) != 0) {
12362ca5b659SJoost Mulders 		/*
12372ca5b659SJoost Mulders 		 * Received some packets.
12382ca5b659SJoost Mulders 		 */
12392ca5b659SJoost Mulders 		lp = vr_receive(vrp);
12402ca5b659SJoost Mulders 
12412ca5b659SJoost Mulders 		/*
12422ca5b659SJoost Mulders 		 * DMA stops after a conflict in the FIFO.
12432ca5b659SJoost Mulders 		 */
12442ca5b659SJoost Mulders 		if ((status & VR_ISR_RX_ERR_BITS) != 0)
12452ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
12462ca5b659SJoost Mulders 		status &= ~(VR_ISR0_RX_DONE | VR_ISR_RX_ERR_BITS);
12472ca5b659SJoost Mulders 	}
12482ca5b659SJoost Mulders 
12492ca5b659SJoost Mulders 	/*
12502ca5b659SJoost Mulders 	 * Transmit completion.
12512ca5b659SJoost Mulders 	 */
12522ca5b659SJoost Mulders 	if ((status & (VR_ISR0_TX_DONE | VR_ISR_TX_ERR_BITS)) != 0) {
12532ca5b659SJoost Mulders 		/*
12542ca5b659SJoost Mulders 		 * Card done with transmitting some packets
12552ca5b659SJoost Mulders 		 * TX_DONE is generated 3 times per ring but it appears
12562ca5b659SJoost Mulders 		 * more often because it is also set when an RX_DONE
12572ca5b659SJoost Mulders 		 * interrupt is generated.
12582ca5b659SJoost Mulders 		 */
12592ca5b659SJoost Mulders 		mutex_enter(&vrp->tx.lock);
12602ca5b659SJoost Mulders 		vr_tx_reclaim(vrp);
12612ca5b659SJoost Mulders 		tx_resched = vrp->tx.resched;
12622ca5b659SJoost Mulders 		vrp->tx.resched = 0;
12632ca5b659SJoost Mulders 		mutex_exit(&vrp->tx.lock);
12642ca5b659SJoost Mulders 		status &= ~(VR_ISR0_TX_DONE | VR_ISR_TX_ERR_BITS);
12652ca5b659SJoost Mulders 	}
12662ca5b659SJoost Mulders 
12672ca5b659SJoost Mulders 	/*
12682ca5b659SJoost Mulders 	 * Link status change.
12692ca5b659SJoost Mulders 	 */
12702ca5b659SJoost Mulders 	if ((status & VR_ICR0_LINKSTATUS) != 0) {
12712ca5b659SJoost Mulders 		/*
12722ca5b659SJoost Mulders 		 * Get new link state and inform the mac layer.
12732ca5b659SJoost Mulders 		 */
12742ca5b659SJoost Mulders 		mutex_enter(&vrp->oplock);
12752ca5b659SJoost Mulders 		mutex_enter(&vrp->tx.lock);
12762ca5b659SJoost Mulders 		vr_link_state(vrp);
12772ca5b659SJoost Mulders 		mutex_exit(&vrp->tx.lock);
12782ca5b659SJoost Mulders 		mutex_exit(&vrp->oplock);
12792ca5b659SJoost Mulders 		status &= ~VR_ICR0_LINKSTATUS;
12802ca5b659SJoost Mulders 		vrp->stats.linkchanges++;
12812ca5b659SJoost Mulders 		link_change = 1;
12822ca5b659SJoost Mulders 	}
12832ca5b659SJoost Mulders 
12842ca5b659SJoost Mulders 	/*
12852ca5b659SJoost Mulders 	 * Bus error.
12862ca5b659SJoost Mulders 	 */
12872ca5b659SJoost Mulders 	if ((status & VR_ISR0_BUSERR) != 0) {
12882ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "bus error occured");
12892ca5b659SJoost Mulders 		vrp->reset = 1;
12902ca5b659SJoost Mulders 		status &= ~VR_ISR0_BUSERR;
12912ca5b659SJoost Mulders 	}
12922ca5b659SJoost Mulders 
12932ca5b659SJoost Mulders 	/*
12942ca5b659SJoost Mulders 	 * We must have handled all things here.
12952ca5b659SJoost Mulders 	 */
12962ca5b659SJoost Mulders 	ASSERT(status == 0);
12972ca5b659SJoost Mulders 	mutex_exit(&vrp->intrlock);
12982ca5b659SJoost Mulders 
12992ca5b659SJoost Mulders 	/*
13002ca5b659SJoost Mulders 	 * Reset the device if requested
13012ca5b659SJoost Mulders 	 * The request can come from the periodic tx check or from the interrupt
13022ca5b659SJoost Mulders 	 * status.
13032ca5b659SJoost Mulders 	 */
13042ca5b659SJoost Mulders 	if (vrp->reset != 0) {
13052ca5b659SJoost Mulders 		vr_error(vrp);
13062ca5b659SJoost Mulders 		vrp->reset = 0;
13072ca5b659SJoost Mulders 	}
13082ca5b659SJoost Mulders 
13092ca5b659SJoost Mulders 	/*
13102ca5b659SJoost Mulders 	 * Pass up the list with received packets.
13112ca5b659SJoost Mulders 	 */
13122ca5b659SJoost Mulders 	if (lp != NULL)
13132ca5b659SJoost Mulders 		mac_rx(vrp->machdl, 0, lp);
13142ca5b659SJoost Mulders 
13152ca5b659SJoost Mulders 	/*
13162ca5b659SJoost Mulders 	 * Inform the upper layer on the linkstatus if there was a change.
13172ca5b659SJoost Mulders 	 */
13182ca5b659SJoost Mulders 	if (link_change != 0)
13192ca5b659SJoost Mulders 		mac_link_update(vrp->machdl,
13202ca5b659SJoost Mulders 		    (link_state_t)vrp->chip.link.state);
13212ca5b659SJoost Mulders 	/*
13222ca5b659SJoost Mulders 	 * Restart transmissions if we were waiting for tx descriptors.
13232ca5b659SJoost Mulders 	 */
13242ca5b659SJoost Mulders 	if (tx_resched == 1)
13252ca5b659SJoost Mulders 		mac_tx_update(vrp->machdl);
13262ca5b659SJoost Mulders 
13272ca5b659SJoost Mulders 	/*
13282ca5b659SJoost Mulders 	 * Read something from the card to ensure that all of our configuration
13292ca5b659SJoost Mulders 	 * writes are delivered to the device before the interrupt is ended.
13302ca5b659SJoost Mulders 	 */
13312ca5b659SJoost Mulders 	(void) VR_GET8(vrp->acc_reg, VR_ETHERADDR);
13322ca5b659SJoost Mulders 	return (DDI_INTR_CLAIMED);
13332ca5b659SJoost Mulders }
13342ca5b659SJoost Mulders 
13352ca5b659SJoost Mulders /*
13362ca5b659SJoost Mulders  * Respond to an unforseen situation by resetting the card and our bookkeeping.
13372ca5b659SJoost Mulders  */
13382ca5b659SJoost Mulders static void
vr_error(vr_t * vrp)13392ca5b659SJoost Mulders vr_error(vr_t *vrp)
13402ca5b659SJoost Mulders {
13412ca5b659SJoost Mulders 	vr_log(vrp, CE_WARN, "resetting MAC.");
13422ca5b659SJoost Mulders 	mutex_enter(&vrp->intrlock);
13432ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
13442ca5b659SJoost Mulders 	mutex_enter(&vrp->tx.lock);
13452ca5b659SJoost Mulders 	(void) vr_stop(vrp);
13462ca5b659SJoost Mulders 	vr_reset(vrp);
13472ca5b659SJoost Mulders 	(void) vr_start(vrp);
13482ca5b659SJoost Mulders 	mutex_exit(&vrp->tx.lock);
13492ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
13502ca5b659SJoost Mulders 	mutex_exit(&vrp->intrlock);
13512ca5b659SJoost Mulders 	vrp->stats.resets++;
13522ca5b659SJoost Mulders }
13532ca5b659SJoost Mulders 
13542ca5b659SJoost Mulders /*
13552ca5b659SJoost Mulders  * Collect received packets in a list.
13562ca5b659SJoost Mulders  */
13572ca5b659SJoost Mulders static mblk_t *
vr_receive(vr_t * vrp)13582ca5b659SJoost Mulders vr_receive(vr_t *vrp)
13592ca5b659SJoost Mulders {
13602ca5b659SJoost Mulders 	mblk_t			*lp, *mp, *np;
13612ca5b659SJoost Mulders 	vr_desc_t		*rxp;
13622ca5b659SJoost Mulders 	vr_data_dma_t		*dmap;
13632ca5b659SJoost Mulders 	uint32_t		pklen;
13642ca5b659SJoost Mulders 	uint32_t		rxstat0;
13652ca5b659SJoost Mulders 	uint32_t		n;
13662ca5b659SJoost Mulders 
13672ca5b659SJoost Mulders 	lp = NULL;
13682ca5b659SJoost Mulders 	n = 0;
13692ca5b659SJoost Mulders 	for (rxp = vrp->rx.rp; ; rxp = rxp->next, n++) {
13702ca5b659SJoost Mulders 		/*
13712ca5b659SJoost Mulders 		 * Sync the descriptor before looking at it.
13722ca5b659SJoost Mulders 		 */
13732ca5b659SJoost Mulders 		(void) ddi_dma_sync(vrp->rxring.handle, rxp->offset,
13742ca5b659SJoost Mulders 		    sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORKERNEL);
13752ca5b659SJoost Mulders 
13762ca5b659SJoost Mulders 		/*
13772ca5b659SJoost Mulders 		 * Get the status from the descriptor.
13782ca5b659SJoost Mulders 		 */
13792ca5b659SJoost Mulders 		rxstat0 = ddi_get32(vrp->rxring.acchdl, &rxp->cdesc->stat0);
13802ca5b659SJoost Mulders 
13812ca5b659SJoost Mulders 		/*
13822ca5b659SJoost Mulders 		 * We're done if the descriptor is owned by the card.
13832ca5b659SJoost Mulders 		 */
13842ca5b659SJoost Mulders 		if ((rxstat0 & VR_RDES0_OWN) != 0)
13852ca5b659SJoost Mulders 			break;
13862ca5b659SJoost Mulders 		else if ((rxstat0 & VR_RDES0_RXOK) != 0) {
13872ca5b659SJoost Mulders 			/*
13882ca5b659SJoost Mulders 			 * Received a good packet
13892ca5b659SJoost Mulders 			 */
13902ca5b659SJoost Mulders 			dmap = &rxp->dmabuf;
13912ca5b659SJoost Mulders 			pklen = (rxstat0 >> 16) - ETHERFCSL;
13922ca5b659SJoost Mulders 
13932ca5b659SJoost Mulders 			/*
13942ca5b659SJoost Mulders 			 * Sync the data.
13952ca5b659SJoost Mulders 			 */
13962ca5b659SJoost Mulders 			(void) ddi_dma_sync(dmap->handle, 0,
13972ca5b659SJoost Mulders 			    pklen, DDI_DMA_SYNC_FORKERNEL);
13982ca5b659SJoost Mulders 
13992ca5b659SJoost Mulders 			/*
14002ca5b659SJoost Mulders 			 * Send a new copied message upstream.
14012ca5b659SJoost Mulders 			 */
14022ca5b659SJoost Mulders 			np = allocb(pklen, 0);
14032ca5b659SJoost Mulders 			if (np != NULL) {
14042ca5b659SJoost Mulders 				bcopy(dmap->buf, np->b_rptr, pklen);
14052ca5b659SJoost Mulders 				np->b_wptr = np->b_rptr + pklen;
14062ca5b659SJoost Mulders 
14072ca5b659SJoost Mulders 				vrp->stats.mac_stat_ipackets++;
14082ca5b659SJoost Mulders 				vrp->stats.mac_stat_rbytes += pklen;
14092ca5b659SJoost Mulders 
14102ca5b659SJoost Mulders 				if ((rxstat0 & VR_RDES0_BAR) != 0)
14112ca5b659SJoost Mulders 					vrp->stats.mac_stat_brdcstrcv++;
14122ca5b659SJoost Mulders 				else if ((rxstat0 & VR_RDES0_MAR) != 0)
14132ca5b659SJoost Mulders 					vrp->stats.mac_stat_multircv++;
14142ca5b659SJoost Mulders 
14152ca5b659SJoost Mulders 				/*
14162ca5b659SJoost Mulders 				 * Link this packet in the list.
14172ca5b659SJoost Mulders 				 */
14182ca5b659SJoost Mulders 				np->b_next = NULL;
14192ca5b659SJoost Mulders 				if (lp == NULL)
14202ca5b659SJoost Mulders 					lp = mp = np;
14212ca5b659SJoost Mulders 				else {
14222ca5b659SJoost Mulders 					mp->b_next = np;
14232ca5b659SJoost Mulders 					mp = np;
14242ca5b659SJoost Mulders 				}
14252ca5b659SJoost Mulders 			} else {
14262ca5b659SJoost Mulders 				vrp->stats.allocbfail++;
14272ca5b659SJoost Mulders 				vrp->stats.mac_stat_norcvbuf++;
14282ca5b659SJoost Mulders 			}
14292ca5b659SJoost Mulders 
14302ca5b659SJoost Mulders 		} else {
14312ca5b659SJoost Mulders 			/*
14322ca5b659SJoost Mulders 			 * Received with errors.
14332ca5b659SJoost Mulders 			 */
14342ca5b659SJoost Mulders 			vrp->stats.mac_stat_ierrors++;
14352ca5b659SJoost Mulders 			if ((rxstat0 & VR_RDES0_FAE) != 0)
14362ca5b659SJoost Mulders 				vrp->stats.ether_stat_align_errors++;
14372ca5b659SJoost Mulders 			if ((rxstat0 & VR_RDES0_CRCERR) != 0)
14382ca5b659SJoost Mulders 				vrp->stats.ether_stat_fcs_errors++;
14392ca5b659SJoost Mulders 			if ((rxstat0 & VR_RDES0_LONG) != 0)
14402ca5b659SJoost Mulders 				vrp->stats.ether_stat_toolong_errors++;
14412ca5b659SJoost Mulders 			if ((rxstat0 & VR_RDES0_RUNT) != 0)
14422ca5b659SJoost Mulders 				vrp->stats.ether_stat_tooshort_errors++;
14432ca5b659SJoost Mulders 			if ((rxstat0 & VR_RDES0_FOV) != 0)
14442ca5b659SJoost Mulders 				vrp->stats.mac_stat_overflows++;
14452ca5b659SJoost Mulders 		}
14462ca5b659SJoost Mulders 
14472ca5b659SJoost Mulders 		/*
14482ca5b659SJoost Mulders 		 * Reset descriptor ownership to the MAC.
14492ca5b659SJoost Mulders 		 */
14502ca5b659SJoost Mulders 		ddi_put32(vrp->rxring.acchdl,
14512ca5b659SJoost Mulders 		    &rxp->cdesc->stat0,
14522ca5b659SJoost Mulders 		    VR_RDES0_OWN);
14532ca5b659SJoost Mulders 		(void) ddi_dma_sync(vrp->rxring.handle,
14542ca5b659SJoost Mulders 		    rxp->offset,
14552ca5b659SJoost Mulders 		    sizeof (vr_chip_desc_t),
14562ca5b659SJoost Mulders 		    DDI_DMA_SYNC_FORDEV);
14572ca5b659SJoost Mulders 	}
14582ca5b659SJoost Mulders 	vrp->rx.rp = rxp;
14592ca5b659SJoost Mulders 
14602ca5b659SJoost Mulders 	/*
14612ca5b659SJoost Mulders 	 * If we do flowcontrol and if the card can transmit pause frames,
14622ca5b659SJoost Mulders 	 * increment the "available receive descriptors" register.
14632ca5b659SJoost Mulders 	 */
14642ca5b659SJoost Mulders 	if (n > 0 && vrp->chip.link.flowctrl == VR_PAUSE_BIDIRECTIONAL) {
14652ca5b659SJoost Mulders 		/*
14662ca5b659SJoost Mulders 		 * Whenever the card moves a fragment to host memory it
14672ca5b659SJoost Mulders 		 * decrements the RXBUFCOUNT register. If the value in the
14682ca5b659SJoost Mulders 		 * register reaches a low watermark, the card transmits a pause
14692ca5b659SJoost Mulders 		 * frame. If the value in this register reaches a high
14702ca5b659SJoost Mulders 		 * watermark, the card sends a "cancel pause" frame
14712ca5b659SJoost Mulders 		 *
14722ca5b659SJoost Mulders 		 * Non-zero values written to this byte register are added
14732ca5b659SJoost Mulders 		 * by the chip to the register's contents, so we must write
14742ca5b659SJoost Mulders 		 * the number of descriptors free'd.
14752ca5b659SJoost Mulders 		 */
14762ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_FCR0_RXBUFCOUNT, MIN(n, 0xFF));
14772ca5b659SJoost Mulders 	}
14782ca5b659SJoost Mulders 	return (lp);
14792ca5b659SJoost Mulders }
14802ca5b659SJoost Mulders 
14812ca5b659SJoost Mulders /*
14822ca5b659SJoost Mulders  * Enqueue a list of packets for transmission
14832ca5b659SJoost Mulders  * Return the packets not transmitted.
14842ca5b659SJoost Mulders  */
14852ca5b659SJoost Mulders mblk_t *
vr_mac_tx_enqueue_list(void * p,mblk_t * mp)14862ca5b659SJoost Mulders vr_mac_tx_enqueue_list(void *p, mblk_t *mp)
14872ca5b659SJoost Mulders {
14882ca5b659SJoost Mulders 	vr_t		*vrp;
14892ca5b659SJoost Mulders 	mblk_t		*nextp;
14902ca5b659SJoost Mulders 
14912ca5b659SJoost Mulders 	vrp = (vr_t *)p;
14922ca5b659SJoost Mulders 	mutex_enter(&vrp->tx.lock);
14932ca5b659SJoost Mulders 	do {
14942ca5b659SJoost Mulders 		if (vrp->tx.nfree == 0) {
14952ca5b659SJoost Mulders 			vrp->stats.ether_stat_defer_xmts++;
14962ca5b659SJoost Mulders 			vrp->tx.resched = 1;
14972ca5b659SJoost Mulders 			break;
14982ca5b659SJoost Mulders 		}
14992ca5b659SJoost Mulders 		nextp = mp->b_next;
15002ca5b659SJoost Mulders 		mp->b_next = mp->b_prev = NULL;
15012ca5b659SJoost Mulders 		vr_tx_enqueue_msg(vrp, mp);
15022ca5b659SJoost Mulders 		mp = nextp;
15032ca5b659SJoost Mulders 		vrp->tx.nfree--;
15042ca5b659SJoost Mulders 	} while (mp != NULL);
15052ca5b659SJoost Mulders 	mutex_exit(&vrp->tx.lock);
15062ca5b659SJoost Mulders 
15072ca5b659SJoost Mulders 	/*
15082ca5b659SJoost Mulders 	 * Tell the chip to poll the TX ring.
15092ca5b659SJoost Mulders 	 */
15102ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
15112ca5b659SJoost Mulders 	return (mp);
15122ca5b659SJoost Mulders }
15132ca5b659SJoost Mulders 
15142ca5b659SJoost Mulders /*
15152ca5b659SJoost Mulders  * Enqueue a message for transmission.
15162ca5b659SJoost Mulders  */
15172ca5b659SJoost Mulders static void
vr_tx_enqueue_msg(vr_t * vrp,mblk_t * mp)15182ca5b659SJoost Mulders vr_tx_enqueue_msg(vr_t *vrp, mblk_t *mp)
15192ca5b659SJoost Mulders {
15202ca5b659SJoost Mulders 	vr_desc_t		*wp;
15212ca5b659SJoost Mulders 	vr_data_dma_t		*dmap;
15222ca5b659SJoost Mulders 	uint32_t		pklen;
15232ca5b659SJoost Mulders 	uint32_t		nextp;
15242ca5b659SJoost Mulders 	int			padlen;
15252ca5b659SJoost Mulders 
15262ca5b659SJoost Mulders 	if ((uchar_t)mp->b_rptr[0] == 0xff &&
15272ca5b659SJoost Mulders 	    (uchar_t)mp->b_rptr[1] == 0xff &&
15282ca5b659SJoost Mulders 	    (uchar_t)mp->b_rptr[2] == 0xff &&
15292ca5b659SJoost Mulders 	    (uchar_t)mp->b_rptr[3] == 0xff &&
15302ca5b659SJoost Mulders 	    (uchar_t)mp->b_rptr[4] == 0xff &&
15312ca5b659SJoost Mulders 	    (uchar_t)mp->b_rptr[5] == 0xff)
15322ca5b659SJoost Mulders 		vrp->stats.mac_stat_brdcstxmt++;
15332ca5b659SJoost Mulders 	else if ((uchar_t)mp->b_rptr[0] == 1)
15342ca5b659SJoost Mulders 		vrp->stats.mac_stat_multixmt++;
15352ca5b659SJoost Mulders 
15362ca5b659SJoost Mulders 	pklen = msgsize(mp);
15372ca5b659SJoost Mulders 	wp = vrp->tx.wp;
15382ca5b659SJoost Mulders 	dmap = &wp->dmabuf;
15392ca5b659SJoost Mulders 
15402ca5b659SJoost Mulders 	/*
15412ca5b659SJoost Mulders 	 * Copy the message into the pre-mapped buffer and free mp
15422ca5b659SJoost Mulders 	 */
15432ca5b659SJoost Mulders 	mcopymsg(mp, dmap->buf);
15442ca5b659SJoost Mulders 
15452ca5b659SJoost Mulders 	/*
15462ca5b659SJoost Mulders 	 * Clean padlen bytes of short packet.
15472ca5b659SJoost Mulders 	 */
15482ca5b659SJoost Mulders 	padlen = ETHERMIN - pklen;
15492ca5b659SJoost Mulders 	if (padlen > 0) {
15502ca5b659SJoost Mulders 		bzero(dmap->buf + pklen, padlen);
15512ca5b659SJoost Mulders 		pklen += padlen;
15522ca5b659SJoost Mulders 	}
15532ca5b659SJoost Mulders 
15542ca5b659SJoost Mulders 	/*
15552ca5b659SJoost Mulders 	 * Most of the statistics are updated on reclaim, after the actual
15562ca5b659SJoost Mulders 	 * transmit. obytes is maintained here because the length is cleared
15572ca5b659SJoost Mulders 	 * after transmission
15582ca5b659SJoost Mulders 	 */
15592ca5b659SJoost Mulders 	vrp->stats.mac_stat_obytes += pklen;
15602ca5b659SJoost Mulders 
15612ca5b659SJoost Mulders 	/*
15622ca5b659SJoost Mulders 	 * Sync the data so the device sees the new content too.
15632ca5b659SJoost Mulders 	 */
15642ca5b659SJoost Mulders 	(void) ddi_dma_sync(dmap->handle, 0, pklen, DDI_DMA_SYNC_FORDEV);
15652ca5b659SJoost Mulders 
15662ca5b659SJoost Mulders 	/*
15672ca5b659SJoost Mulders 	 * If we have reached the TX interrupt distance, enable a TX interrupt
15682ca5b659SJoost Mulders 	 * for this packet. The Interrupt Control (IC) bit in the transmit
15692ca5b659SJoost Mulders 	 * descriptor doesn't have any effect on the interrupt generation
15702ca5b659SJoost Mulders 	 * despite the vague statements in the datasheet. Thus, we use the
15712ca5b659SJoost Mulders 	 * more obscure interrupt suppress bit which is probably part of the
15722ca5b659SJoost Mulders 	 * MAC's bookkeeping for TX interrupts and fragmented packets.
15732ca5b659SJoost Mulders 	 */
15742ca5b659SJoost Mulders 	vrp->tx.intr_distance++;
15752ca5b659SJoost Mulders 	nextp = ddi_get32(vrp->txring.acchdl, &wp->cdesc->next);
15762ca5b659SJoost Mulders 	if (vrp->tx.intr_distance >= VR_TX_MAX_INTR_DISTANCE) {
15772ca5b659SJoost Mulders 		/*
15782ca5b659SJoost Mulders 		 * Don't suppress the interrupt for this packet.
15792ca5b659SJoost Mulders 		 */
15802ca5b659SJoost Mulders 		vrp->tx.intr_distance = 0;
15812ca5b659SJoost Mulders 		nextp &= (~VR_TDES3_SUPPRESS_INTR);
15822ca5b659SJoost Mulders 	} else {
15832ca5b659SJoost Mulders 		/*
15842ca5b659SJoost Mulders 		 * Suppress the interrupt for this packet.
15852ca5b659SJoost Mulders 		 */
15862ca5b659SJoost Mulders 		nextp |= VR_TDES3_SUPPRESS_INTR;
15872ca5b659SJoost Mulders 	}
15882ca5b659SJoost Mulders 
15892ca5b659SJoost Mulders 	/*
15902ca5b659SJoost Mulders 	 * Write and sync the chip's descriptor
15912ca5b659SJoost Mulders 	 */
15922ca5b659SJoost Mulders 	ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat1,
15932ca5b659SJoost Mulders 	    pklen | (VR_TDES1_STP | VR_TDES1_EDP | VR_TDES1_CHN));
15942ca5b659SJoost Mulders 	ddi_put32(vrp->txring.acchdl, &wp->cdesc->next, nextp);
15952ca5b659SJoost Mulders 	ddi_put32(vrp->txring.acchdl, &wp->cdesc->stat0, VR_TDES0_OWN);
15962ca5b659SJoost Mulders 	(void) ddi_dma_sync(vrp->txring.handle, wp->offset,
15972ca5b659SJoost Mulders 	    sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
15982ca5b659SJoost Mulders 
15992ca5b659SJoost Mulders 	/*
16002ca5b659SJoost Mulders 	 * The ticks counter is cleared by reclaim when it reclaimed some
16012ca5b659SJoost Mulders 	 * descriptors and incremented by the periodic TX stall check.
16022ca5b659SJoost Mulders 	 */
16032ca5b659SJoost Mulders 	vrp->tx.stallticks = 1;
16042ca5b659SJoost Mulders 	vrp->tx.wp = wp->next;
16052ca5b659SJoost Mulders }
16062ca5b659SJoost Mulders 
16072ca5b659SJoost Mulders /*
16082ca5b659SJoost Mulders  * Free transmitted descriptors.
16092ca5b659SJoost Mulders  */
16102ca5b659SJoost Mulders static void
vr_tx_reclaim(vr_t * vrp)16112ca5b659SJoost Mulders vr_tx_reclaim(vr_t *vrp)
16122ca5b659SJoost Mulders {
16132ca5b659SJoost Mulders 	vr_desc_t		*cp;
16142ca5b659SJoost Mulders 	uint32_t		stat0, stat1, freed, dirty;
16152ca5b659SJoost Mulders 
16162ca5b659SJoost Mulders 	ASSERT(mutex_owned(&vrp->tx.lock));
16172ca5b659SJoost Mulders 
16182ca5b659SJoost Mulders 	freed = 0;
16192ca5b659SJoost Mulders 	dirty = vrp->tx.ndesc - vrp->tx.nfree;
16202ca5b659SJoost Mulders 	for (cp = vrp->tx.cp; dirty > 0; cp = cp->next) {
16212ca5b659SJoost Mulders 		/*
16222ca5b659SJoost Mulders 		 * Sync & get descriptor status.
16232ca5b659SJoost Mulders 		 */
16242ca5b659SJoost Mulders 		(void) ddi_dma_sync(vrp->txring.handle, cp->offset,
16252ca5b659SJoost Mulders 		    sizeof (vr_chip_desc_t),
16262ca5b659SJoost Mulders 		    DDI_DMA_SYNC_FORKERNEL);
16272ca5b659SJoost Mulders 		stat0 = ddi_get32(vrp->txring.acchdl, &cp->cdesc->stat0);
16282ca5b659SJoost Mulders 
16292ca5b659SJoost Mulders 		if ((stat0 & VR_TDES0_OWN) != 0)
16302ca5b659SJoost Mulders 			break;
16312ca5b659SJoost Mulders 
16322ca5b659SJoost Mulders 		/*
16332ca5b659SJoost Mulders 		 * Do stats for the first descriptor in a chain.
16342ca5b659SJoost Mulders 		 */
16352ca5b659SJoost Mulders 		stat1 = ddi_get32(vrp->txring.acchdl, &cp->cdesc->stat1);
16362ca5b659SJoost Mulders 		if ((stat1 & VR_TDES1_STP) != 0) {
16372ca5b659SJoost Mulders 			if ((stat0 & VR_TDES0_TERR) != 0) {
16382ca5b659SJoost Mulders 				vrp->stats.ether_stat_macxmt_errors++;
16392ca5b659SJoost Mulders 				if ((stat0 & VR_TDES0_UDF) != 0)
16402ca5b659SJoost Mulders 					vrp->stats.mac_stat_underflows++;
16412ca5b659SJoost Mulders 				if ((stat0 & VR_TDES0_ABT) != 0)
16422ca5b659SJoost Mulders 					vrp-> stats.ether_stat_ex_collisions++;
16432ca5b659SJoost Mulders 				/*
16442ca5b659SJoost Mulders 				 * Abort and FIFO underflow stop the MAC.
16452ca5b659SJoost Mulders 				 * Packet queueing must be disabled with HD
16462ca5b659SJoost Mulders 				 * links because otherwise the MAC is also lost
16472ca5b659SJoost Mulders 				 * after a few of these events.
16482ca5b659SJoost Mulders 				 */
16492ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CTRL0,
16502ca5b659SJoost Mulders 				    VR_CTRL0_DMA_GO);
16512ca5b659SJoost Mulders 			} else
16522ca5b659SJoost Mulders 				vrp->stats.mac_stat_opackets++;
16532ca5b659SJoost Mulders 
16542ca5b659SJoost Mulders 			if ((stat0 & VR_TDES0_COL) != 0) {
16552ca5b659SJoost Mulders 				if ((stat0 & VR_TDES0_NCR) == 1) {
16562ca5b659SJoost Mulders 					vrp->stats.
16572ca5b659SJoost Mulders 					    ether_stat_first_collisions++;
16582ca5b659SJoost Mulders 				} else {
16592ca5b659SJoost Mulders 					vrp->stats.
16602ca5b659SJoost Mulders 					    ether_stat_multi_collisions++;
16612ca5b659SJoost Mulders 				}
16622ca5b659SJoost Mulders 				vrp->stats.mac_stat_collisions +=
16632ca5b659SJoost Mulders 				    (stat0 & VR_TDES0_NCR);
16642ca5b659SJoost Mulders 			}
16652ca5b659SJoost Mulders 
16662ca5b659SJoost Mulders 			if ((stat0 & VR_TDES0_CRS) != 0)
16672ca5b659SJoost Mulders 				vrp->stats.ether_stat_carrier_errors++;
16682ca5b659SJoost Mulders 
16692ca5b659SJoost Mulders 			if ((stat0 & VR_TDES0_OWC) != 0)
16702ca5b659SJoost Mulders 				vrp->stats.ether_stat_tx_late_collisions++;
16712ca5b659SJoost Mulders 		}
16722ca5b659SJoost Mulders 		freed += 1;
16732ca5b659SJoost Mulders 		dirty -= 1;
16742ca5b659SJoost Mulders 	}
16752ca5b659SJoost Mulders 	vrp->tx.cp = cp;
16762ca5b659SJoost Mulders 
16772ca5b659SJoost Mulders 	if (freed > 0) {
16782ca5b659SJoost Mulders 		vrp->tx.nfree += freed;
16792ca5b659SJoost Mulders 		vrp->tx.stallticks = 0;
16802ca5b659SJoost Mulders 		vrp->stats.txreclaims += 1;
16812ca5b659SJoost Mulders 	} else
16822ca5b659SJoost Mulders 		vrp->stats.txreclaim0 += 1;
16832ca5b659SJoost Mulders }
16842ca5b659SJoost Mulders 
16852ca5b659SJoost Mulders /*
16862ca5b659SJoost Mulders  * Check TX health every 2 seconds.
16872ca5b659SJoost Mulders  */
16882ca5b659SJoost Mulders static void
vr_periodic(void * p)16892ca5b659SJoost Mulders vr_periodic(void *p)
16902ca5b659SJoost Mulders {
16912ca5b659SJoost Mulders 	vr_t		*vrp;
16922ca5b659SJoost Mulders 
16932ca5b659SJoost Mulders 	vrp = (vr_t *)p;
16942ca5b659SJoost Mulders 	if (vrp->chip.state == CHIPSTATE_RUNNING &&
16952ca5b659SJoost Mulders 	    vrp->chip.link.state == VR_LINK_STATE_UP && vrp->reset == 0) {
16962ca5b659SJoost Mulders 		if (mutex_tryenter(&vrp->intrlock) != 0) {
16972ca5b659SJoost Mulders 			mutex_enter(&vrp->tx.lock);
16982ca5b659SJoost Mulders 			if (vrp->tx.resched == 1) {
16992ca5b659SJoost Mulders 				if (vrp->tx.stallticks >= VR_MAXTXCHECKS) {
17002ca5b659SJoost Mulders 					/*
17012ca5b659SJoost Mulders 					 * No succesful reclaim in the last n
17022ca5b659SJoost Mulders 					 * intervals. Reset the MAC.
17032ca5b659SJoost Mulders 					 */
17042ca5b659SJoost Mulders 					vrp->reset = 1;
17052ca5b659SJoost Mulders 					vr_log(vrp, CE_WARN,
17062ca5b659SJoost Mulders 					    "TX stalled, resetting MAC");
170715c07adcSJohn Levon 					vrp->stats.txstalls++;
17082ca5b659SJoost Mulders 				} else {
17092ca5b659SJoost Mulders 					/*
17102ca5b659SJoost Mulders 					 * Increase until we find that we've
17112ca5b659SJoost Mulders 					 * waited long enough.
17122ca5b659SJoost Mulders 					 */
17132ca5b659SJoost Mulders 					vrp->tx.stallticks += 1;
17142ca5b659SJoost Mulders 				}
17152ca5b659SJoost Mulders 			}
17162ca5b659SJoost Mulders 			mutex_exit(&vrp->tx.lock);
17172ca5b659SJoost Mulders 			mutex_exit(&vrp->intrlock);
17182ca5b659SJoost Mulders 			vrp->stats.txchecks++;
17192ca5b659SJoost Mulders 		}
17202ca5b659SJoost Mulders 	}
17212ca5b659SJoost Mulders 	vrp->stats.cyclics++;
17222ca5b659SJoost Mulders }
17232ca5b659SJoost Mulders 
17242ca5b659SJoost Mulders /*
17252ca5b659SJoost Mulders  * Bring the device to our desired initial state.
17262ca5b659SJoost Mulders  */
17272ca5b659SJoost Mulders static void
vr_reset(vr_t * vrp)17282ca5b659SJoost Mulders vr_reset(vr_t *vrp)
17292ca5b659SJoost Mulders {
17302ca5b659SJoost Mulders 	uint32_t	time;
17312ca5b659SJoost Mulders 
17322ca5b659SJoost Mulders 	/*
17332ca5b659SJoost Mulders 	 * Reset the MAC
17342ca5b659SJoost Mulders 	 * If we don't wait long enough for the forced reset to complete,
17352ca5b659SJoost Mulders 	 * MAC looses sync with PHY. Result link up, no link change interrupt
17362ca5b659SJoost Mulders 	 * and no data transfer.
17372ca5b659SJoost Mulders 	 */
17382ca5b659SJoost Mulders 	time = 0;
17392ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_io, VR_CTRL1, VR_CTRL1_RESET);
17402ca5b659SJoost Mulders 	do {
17412ca5b659SJoost Mulders 		drv_usecwait(100);
17422ca5b659SJoost Mulders 		time += 100;
17432ca5b659SJoost Mulders 		if (time >= 100000) {
17442ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_io, VR_MISC1, VR_MISC1_RESET);
17452ca5b659SJoost Mulders 			delay(drv_usectohz(200000));
17462ca5b659SJoost Mulders 		}
17472ca5b659SJoost Mulders 	} while ((VR_GET8(vrp->acc_io, VR_CTRL1) & VR_CTRL1_RESET) != 0);
17482ca5b659SJoost Mulders 	delay(drv_usectohz(10000));
17492ca5b659SJoost Mulders 
17502ca5b659SJoost Mulders 	/*
17512ca5b659SJoost Mulders 	 * Load the PROM contents into the MAC again.
17522ca5b659SJoost Mulders 	 */
17532ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_io, VR_PROMCTL, VR_PROMCTL_RELOAD);
17542ca5b659SJoost Mulders 	delay(drv_usectohz(100000));
17552ca5b659SJoost Mulders 
17562ca5b659SJoost Mulders 	/*
17572ca5b659SJoost Mulders 	 * Tell the MAC via IO space that we like to use memory space for
17582ca5b659SJoost Mulders 	 * accessing registers.
17592ca5b659SJoost Mulders 	 */
17602ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_io, VR_CFGD, VR_CFGD_MMIOEN);
17612ca5b659SJoost Mulders }
17622ca5b659SJoost Mulders 
17632ca5b659SJoost Mulders /*
17642ca5b659SJoost Mulders  * Prepare and enable the card (MAC + PHY + PCI).
17652ca5b659SJoost Mulders  */
17662ca5b659SJoost Mulders static int
vr_start(vr_t * vrp)17672ca5b659SJoost Mulders vr_start(vr_t *vrp)
17682ca5b659SJoost Mulders {
17692ca5b659SJoost Mulders 	uint8_t		pci_latency, pci_mode;
17702ca5b659SJoost Mulders 
17712ca5b659SJoost Mulders 	ASSERT(mutex_owned(&vrp->oplock));
17722ca5b659SJoost Mulders 
17732ca5b659SJoost Mulders 	/*
17742ca5b659SJoost Mulders 	 * Allocate DMA buffers for RX.
17752ca5b659SJoost Mulders 	 */
17762ca5b659SJoost Mulders 	if (vr_rxring_init(vrp) != VR_SUCCESS) {
17772ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "vr_rxring_init() failed");
17782ca5b659SJoost Mulders 		return (ENOMEM);
17792ca5b659SJoost Mulders 	}
17802ca5b659SJoost Mulders 
17812ca5b659SJoost Mulders 	/*
17822ca5b659SJoost Mulders 	 * Allocate DMA buffers for TX.
17832ca5b659SJoost Mulders 	 */
17842ca5b659SJoost Mulders 	if (vr_txring_init(vrp) != VR_SUCCESS) {
17852ca5b659SJoost Mulders 		vr_log(vrp, CE_NOTE, "vr_txring_init() failed");
17862ca5b659SJoost Mulders 		vr_rxring_fini(vrp);
17872ca5b659SJoost Mulders 		return (ENOMEM);
17882ca5b659SJoost Mulders 	}
17892ca5b659SJoost Mulders 
17902ca5b659SJoost Mulders 	/*
17912ca5b659SJoost Mulders 	 * Changes of the chip specific registers as done in VIA's fet driver
17922ca5b659SJoost Mulders 	 * These bits are not in the datasheet and controlled by vr_chip_info.
17932ca5b659SJoost Mulders 	 */
17942ca5b659SJoost Mulders 	pci_mode = VR_GET8(vrp->acc_reg, VR_MODE2);
17952ca5b659SJoost Mulders 	if ((vrp->chip.info.bugs & VR_BUG_NEEDMODE10T) != 0)
17962ca5b659SJoost Mulders 		pci_mode |= VR_MODE2_MODE10T;
17972ca5b659SJoost Mulders 
17982ca5b659SJoost Mulders 	if ((vrp->chip.info.bugs & VR_BUG_NEEDMODE2PCEROPT) != 0)
17992ca5b659SJoost Mulders 		pci_mode |= VR_MODE2_PCEROPT;
18002ca5b659SJoost Mulders 
18012ca5b659SJoost Mulders 	if ((vrp->chip.info.features & VR_FEATURE_MRDLNMULTIPLE) != 0)
18022ca5b659SJoost Mulders 		pci_mode |= VR_MODE2_MRDPL;
18032ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_MODE2, pci_mode);
18042ca5b659SJoost Mulders 
18052ca5b659SJoost Mulders 	pci_mode = VR_GET8(vrp->acc_reg, VR_MODE3);
18062ca5b659SJoost Mulders 	if ((vrp->chip.info.bugs & VR_BUG_NEEDMIION) != 0)
18072ca5b659SJoost Mulders 		pci_mode |= VR_MODE3_MIION;
18082ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_MODE3, pci_mode);
18092ca5b659SJoost Mulders 
18102ca5b659SJoost Mulders 	/*
18112ca5b659SJoost Mulders 	 * RX: Accept broadcast packets.
18122ca5b659SJoost Mulders 	 */
18132ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTBROAD);
18142ca5b659SJoost Mulders 
18152ca5b659SJoost Mulders 	/*
18162ca5b659SJoost Mulders 	 * RX: Start DMA when there are 256 bytes in the FIFO.
18172ca5b659SJoost Mulders 	 */
18182ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_FIFO_THRESHOLD_BITS,
18192ca5b659SJoost Mulders 	    VR_RXCFG_FIFO_THRESHOLD_256);
18202ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_BCR0, VR_BCR0_RX_FIFO_THRESHOLD_BITS,
18212ca5b659SJoost Mulders 	    VR_BCR0_RX_FIFO_THRESHOLD_256);
18222ca5b659SJoost Mulders 
18232ca5b659SJoost Mulders 	/*
18242ca5b659SJoost Mulders 	 * TX: Start transmit when there are 256 bytes in the FIFO.
18252ca5b659SJoost Mulders 	 */
18262ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_TXCFG, VR_TXCFG_FIFO_THRESHOLD_BITS,
18272ca5b659SJoost Mulders 	    VR_TXCFG_FIFO_THRESHOLD_256);
18282ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_BCR1, VR_BCR1_TX_FIFO_THRESHOLD_BITS,
18292ca5b659SJoost Mulders 	    VR_BCR1_TX_FIFO_THRESHOLD_256);
18302ca5b659SJoost Mulders 
18312ca5b659SJoost Mulders 	/*
18322ca5b659SJoost Mulders 	 * Burst transfers up to 256 bytes.
18332ca5b659SJoost Mulders 	 */
18342ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_BCR0, VR_BCR0_DMABITS, VR_BCR0_DMA256);
18352ca5b659SJoost Mulders 
18362ca5b659SJoost Mulders 	/*
18372ca5b659SJoost Mulders 	 * Disable TX autopolling as it is bad for RX performance
18382ca5b659SJoost Mulders 	 * I assume this is because the RX process finds the bus often occupied
18392ca5b659SJoost Mulders 	 * by the polling process.
18402ca5b659SJoost Mulders 	 */
18412ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_NOAUTOPOLL);
18422ca5b659SJoost Mulders 
18432ca5b659SJoost Mulders 	/*
18442ca5b659SJoost Mulders 	 * Honor the PCI latency timer if it is reasonable.
18452ca5b659SJoost Mulders 	 */
18462ca5b659SJoost Mulders 	pci_latency = VR_GET8(vrp->acc_cfg, PCI_CONF_LATENCY_TIMER);
18472ca5b659SJoost Mulders 	if (pci_latency != 0 && pci_latency != 0xFF)
18482ca5b659SJoost Mulders 		VR_SETBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_LATENCYTIMER);
18492ca5b659SJoost Mulders 	else
18502ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_LATENCYTIMER);
18512ca5b659SJoost Mulders 
18522ca5b659SJoost Mulders 	/*
18532ca5b659SJoost Mulders 	 * Ensure that VLAN filtering is off, because this strips the tag.
18542ca5b659SJoost Mulders 	 */
18552ca5b659SJoost Mulders 	if ((vrp->chip.info.features & VR_FEATURE_VLANTAGGING) != 0) {
18562ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg, VR_BCR1, VR_BCR1_VLANFILTER);
18572ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg, VR_TXCFG, VR_TXCFG_8021PQ_EN);
18582ca5b659SJoost Mulders 	}
18592ca5b659SJoost Mulders 
18602ca5b659SJoost Mulders 	/*
18612ca5b659SJoost Mulders 	 * Clear the CAM filter.
18622ca5b659SJoost Mulders 	 */
18632ca5b659SJoost Mulders 	if ((vrp->chip.info.features & VR_FEATURE_CAMSUPPORT) != 0) {
18642ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_ENABLE);
18652ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_CAM_MASK, 0);
18662ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
18672ca5b659SJoost Mulders 
18682ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
18692ca5b659SJoost Mulders 		    VR_CAM_CTRL_ENABLE|VR_CAM_CTRL_SELECT_VLAN);
18702ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_VCAM0, 0);
18712ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_VCAM1, 0);
18722ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_WRITE);
18732ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_CAM_MASK, 1);
18742ca5b659SJoost Mulders 		drv_usecwait(2);
18752ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
18762ca5b659SJoost Mulders 	}
18772ca5b659SJoost Mulders 
18782ca5b659SJoost Mulders 	/*
18792ca5b659SJoost Mulders 	 * Give the start addresses of the descriptor rings to the DMA
18802ca5b659SJoost Mulders 	 * controller on the MAC.
18812ca5b659SJoost Mulders 	 */
18822ca5b659SJoost Mulders 	VR_PUT32(vrp->acc_reg, VR_RXADDR, vrp->rx.rp->paddr);
18832ca5b659SJoost Mulders 	VR_PUT32(vrp->acc_reg, VR_TXADDR, vrp->tx.wp->paddr);
18842ca5b659SJoost Mulders 
18852ca5b659SJoost Mulders 	/*
18862ca5b659SJoost Mulders 	 * We don't use the additionally invented interrupt ICR1 register,
18872ca5b659SJoost Mulders 	 * so make sure these are disabled.
18882ca5b659SJoost Mulders 	 */
18892ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_ISR1, 0xFF);
18902ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
18912ca5b659SJoost Mulders 
18922ca5b659SJoost Mulders 	/*
18932ca5b659SJoost Mulders 	 * Enable interrupts.
18942ca5b659SJoost Mulders 	 */
18952ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_ISR0, 0xFFFF);
18962ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_ICR0, VR_ICR0_CFG);
18972ca5b659SJoost Mulders 
18982ca5b659SJoost Mulders 	/*
18992ca5b659SJoost Mulders 	 * Enable the DMA controller.
19002ca5b659SJoost Mulders 	 */
19012ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_GO);
19022ca5b659SJoost Mulders 
19032ca5b659SJoost Mulders 	/*
19042ca5b659SJoost Mulders 	 * Configure the link. Rely on the link change interrupt for getting
19052ca5b659SJoost Mulders 	 * the link state into the driver.
19062ca5b659SJoost Mulders 	 */
19072ca5b659SJoost Mulders 	vr_link_init(vrp);
19082ca5b659SJoost Mulders 
19092ca5b659SJoost Mulders 	/*
19102ca5b659SJoost Mulders 	 * Set the software view on the state to 'running'.
19112ca5b659SJoost Mulders 	 */
19122ca5b659SJoost Mulders 	vrp->chip.state = CHIPSTATE_RUNNING;
19132ca5b659SJoost Mulders 	return (0);
19142ca5b659SJoost Mulders }
19152ca5b659SJoost Mulders 
19162ca5b659SJoost Mulders /*
19172ca5b659SJoost Mulders  * Stop DMA and interrupts.
19182ca5b659SJoost Mulders  */
19192ca5b659SJoost Mulders static int
vr_stop(vr_t * vrp)19202ca5b659SJoost Mulders vr_stop(vr_t *vrp)
19212ca5b659SJoost Mulders {
19222ca5b659SJoost Mulders 	ASSERT(mutex_owned(&vrp->oplock));
19232ca5b659SJoost Mulders 
19242ca5b659SJoost Mulders 	/*
19252ca5b659SJoost Mulders 	 * Stop interrupts.
19262ca5b659SJoost Mulders 	 */
19272ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_ICR0, 0);
19282ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_ICR1, 0);
19292ca5b659SJoost Mulders 
19302ca5b659SJoost Mulders 	/*
19312ca5b659SJoost Mulders 	 * Stop DMA.
19322ca5b659SJoost Mulders 	 */
19332ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_CTRL0, VR_CTRL0_DMA_STOP);
19342ca5b659SJoost Mulders 
19352ca5b659SJoost Mulders 	/*
19362ca5b659SJoost Mulders 	 * Set the software view on the state to stopped.
19372ca5b659SJoost Mulders 	 */
19382ca5b659SJoost Mulders 	vrp->chip.state = CHIPSTATE_STOPPED;
19392ca5b659SJoost Mulders 
19402ca5b659SJoost Mulders 	/*
19412ca5b659SJoost Mulders 	 * Remove DMA buffers from the rings.
19422ca5b659SJoost Mulders 	 */
19432ca5b659SJoost Mulders 	vr_rxring_fini(vrp);
19442ca5b659SJoost Mulders 	vr_txring_fini(vrp);
19452ca5b659SJoost Mulders 	return (0);
19462ca5b659SJoost Mulders }
19472ca5b659SJoost Mulders 
19482ca5b659SJoost Mulders int
vr_mac_start(void * p)19492ca5b659SJoost Mulders vr_mac_start(void *p)
19502ca5b659SJoost Mulders {
19512ca5b659SJoost Mulders 	vr_t	*vrp;
19522ca5b659SJoost Mulders 	int	rc;
19532ca5b659SJoost Mulders 
19542ca5b659SJoost Mulders 	vrp = (vr_t *)p;
19552ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
19562ca5b659SJoost Mulders 
19572ca5b659SJoost Mulders 	/*
19582ca5b659SJoost Mulders 	 * Reset the card.
19592ca5b659SJoost Mulders 	 */
19602ca5b659SJoost Mulders 	vr_reset(vrp);
19612ca5b659SJoost Mulders 
19622ca5b659SJoost Mulders 	/*
19632ca5b659SJoost Mulders 	 * Prepare and enable the card.
19642ca5b659SJoost Mulders 	 */
19652ca5b659SJoost Mulders 	rc = vr_start(vrp);
19662ca5b659SJoost Mulders 
19672ca5b659SJoost Mulders 	/*
19682ca5b659SJoost Mulders 	 * Configure a cyclic function to keep the card & driver from diverting.
19692ca5b659SJoost Mulders 	 */
19702ca5b659SJoost Mulders 	vrp->periodic_id =
19712ca5b659SJoost Mulders 	    ddi_periodic_add(vr_periodic, vrp, VR_CHECK_INTERVAL, DDI_IPL_0);
19722ca5b659SJoost Mulders 
19732ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
19742ca5b659SJoost Mulders 	return (rc);
19752ca5b659SJoost Mulders }
19762ca5b659SJoost Mulders 
19772ca5b659SJoost Mulders void
vr_mac_stop(void * p)19782ca5b659SJoost Mulders vr_mac_stop(void *p)
19792ca5b659SJoost Mulders {
19802ca5b659SJoost Mulders 	vr_t	*vrp = p;
19812ca5b659SJoost Mulders 
19822ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
19832ca5b659SJoost Mulders 	mutex_enter(&vrp->tx.lock);
19842ca5b659SJoost Mulders 
19852ca5b659SJoost Mulders 	/*
19862ca5b659SJoost Mulders 	 * Stop the device.
19872ca5b659SJoost Mulders 	 */
19882ca5b659SJoost Mulders 	(void) vr_stop(vrp);
19892ca5b659SJoost Mulders 	mutex_exit(&vrp->tx.lock);
19902ca5b659SJoost Mulders 
19912ca5b659SJoost Mulders 	/*
19922ca5b659SJoost Mulders 	 * Remove the cyclic from the system.
19932ca5b659SJoost Mulders 	 */
19942ca5b659SJoost Mulders 	ddi_periodic_delete(vrp->periodic_id);
19952ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
19962ca5b659SJoost Mulders }
19972ca5b659SJoost Mulders 
19982ca5b659SJoost Mulders /*
19992ca5b659SJoost Mulders  * Add or remove a multicast address to/from the filter
20002ca5b659SJoost Mulders  *
20012ca5b659SJoost Mulders  * From the 21143 manual:
20022ca5b659SJoost Mulders  *  The 21143 can store 512 bits serving as hash bucket heads, and one physical
20032ca5b659SJoost Mulders  *  48-bit Ethernet address. Incoming frames with multicast destination
20042ca5b659SJoost Mulders  *  addresses are subjected to imperfect filtering. Frames with physical
20052ca5b659SJoost Mulders  *  destination  addresses are checked against the single physical address.
20062ca5b659SJoost Mulders  *  For any incoming frame with a multicast destination address, the 21143
20072ca5b659SJoost Mulders  *  applies the standard Ethernet cyclic redundancy check (CRC) function to the
20082ca5b659SJoost Mulders  *  first 6 bytes containing the destination address, then it uses the most
20092ca5b659SJoost Mulders  *  significant 9 bits of the result as a bit index into the table. If the
20102ca5b659SJoost Mulders  *  indexed bit is set, the frame is accepted. If the bit is cleared, the frame
20112ca5b659SJoost Mulders  *  is rejected. This filtering mode is called imperfect because multicast
20122ca5b659SJoost Mulders  *  frames not addressed to this station may slip through, but it still
20132ca5b659SJoost Mulders  *  decreases the number of frames that the host can receive.
20142ca5b659SJoost Mulders  * I assume the above is also the way the VIA chips work. There's not a single
20152ca5b659SJoost Mulders  * word about the multicast filter in the datasheet.
20162ca5b659SJoost Mulders  *
20172ca5b659SJoost Mulders  * Another word on the CAM filter on VT6105M controllers:
20182ca5b659SJoost Mulders  *  The VT6105M has content addressable memory which can be used for perfect
20192ca5b659SJoost Mulders  *  filtering of 32 multicast addresses and a few VLAN id's
20202ca5b659SJoost Mulders  *
20212ca5b659SJoost Mulders  *  I think it works like this: When the controller receives a multicast
20222ca5b659SJoost Mulders  *  address, it looks up the address using CAM. When it is found, it takes the
20232ca5b659SJoost Mulders  *  matching cell address (index) and compares this to the bit position in the
20242ca5b659SJoost Mulders  *  cam mask. If the bit is set, the packet is passed up. If CAM lookup does not
20252ca5b659SJoost Mulders  *  result in a match, the packet is filtered using the hash based filter,
20262ca5b659SJoost Mulders  *  if that matches, the packet is passed up and dropped otherwise
20272ca5b659SJoost Mulders  * Also, there's not a single word in the datasheet on how this cam is supposed
20282ca5b659SJoost Mulders  * to work ...
20292ca5b659SJoost Mulders  */
20302ca5b659SJoost Mulders int
vr_mac_set_multicast(void * p,boolean_t add,const uint8_t * mca)20312ca5b659SJoost Mulders vr_mac_set_multicast(void *p, boolean_t add, const uint8_t *mca)
20322ca5b659SJoost Mulders {
20332ca5b659SJoost Mulders 	vr_t		*vrp;
20342ca5b659SJoost Mulders 	uint32_t	crc_index;
20352ca5b659SJoost Mulders 	int32_t		cam_index;
20362ca5b659SJoost Mulders 	uint32_t	cam_mask;
20372ca5b659SJoost Mulders 	boolean_t	use_hash_filter;
20382ca5b659SJoost Mulders 	ether_addr_t	taddr;
20392ca5b659SJoost Mulders 	uint32_t	a;
20402ca5b659SJoost Mulders 
20412ca5b659SJoost Mulders 	vrp = (vr_t *)p;
20422ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
20432ca5b659SJoost Mulders 	mutex_enter(&vrp->intrlock);
20442ca5b659SJoost Mulders 	use_hash_filter = B_FALSE;
20452ca5b659SJoost Mulders 
20462ca5b659SJoost Mulders 	if ((vrp->chip.info.features & VR_FEATURE_CAMSUPPORT) != 0) {
20472ca5b659SJoost Mulders 		/*
20482ca5b659SJoost Mulders 		 * Program the perfect filter.
20492ca5b659SJoost Mulders 		 */
20502ca5b659SJoost Mulders 		cam_mask = VR_GET32(vrp->acc_reg, VR_CAM_MASK);
20512ca5b659SJoost Mulders 		if (add == B_TRUE) {
20522ca5b659SJoost Mulders 			/*
20532ca5b659SJoost Mulders 			 * Get index of first empty slot.
20542ca5b659SJoost Mulders 			 */
20552ca5b659SJoost Mulders 			bzero(&taddr, sizeof (taddr));
20562ca5b659SJoost Mulders 			cam_index = vr_cam_index(vrp, taddr);
20572ca5b659SJoost Mulders 			if (cam_index != -1) {
20582ca5b659SJoost Mulders 				/*
20592ca5b659SJoost Mulders 				 * Add address at cam_index.
20602ca5b659SJoost Mulders 				 */
20612ca5b659SJoost Mulders 				cam_mask |= (1 << cam_index);
20622ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
20632ca5b659SJoost Mulders 				    VR_CAM_CTRL_ENABLE);
20642ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_ADDR, cam_index);
20652ca5b659SJoost Mulders 				VR_PUT32(vrp->acc_reg, VR_CAM_MASK, cam_mask);
20662ca5b659SJoost Mulders 				for (a = 0; a < ETHERADDRL; a++) {
20672ca5b659SJoost Mulders 					VR_PUT8(vrp->acc_reg,
20682ca5b659SJoost Mulders 					    VR_MCAM0 + a, mca[a]);
20692ca5b659SJoost Mulders 				}
20702ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
20712ca5b659SJoost Mulders 				    VR_CAM_CTRL_WRITE);
20722ca5b659SJoost Mulders 				drv_usecwait(2);
20732ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
20742ca5b659SJoost Mulders 				    VR_CAM_CTRL_DONE);
20752ca5b659SJoost Mulders 			} else {
20762ca5b659SJoost Mulders 				/*
20772ca5b659SJoost Mulders 				 * No free CAM slots available
20782ca5b659SJoost Mulders 				 * Add mca to the imperfect filter.
20792ca5b659SJoost Mulders 				 */
20802ca5b659SJoost Mulders 				use_hash_filter = B_TRUE;
20812ca5b659SJoost Mulders 			}
20822ca5b659SJoost Mulders 		} else {
20832ca5b659SJoost Mulders 			/*
20842ca5b659SJoost Mulders 			 * Find the index of the entry to remove
20852ca5b659SJoost Mulders 			 * If the entry was not found (-1), the addition was
20862ca5b659SJoost Mulders 			 * probably done when the table was full.
20872ca5b659SJoost Mulders 			 */
20882ca5b659SJoost Mulders 			cam_index = vr_cam_index(vrp, mca);
20892ca5b659SJoost Mulders 			if (cam_index != -1) {
20902ca5b659SJoost Mulders 				/*
20912ca5b659SJoost Mulders 				 * Disable the corresponding mask bit.
20922ca5b659SJoost Mulders 				 */
20932ca5b659SJoost Mulders 				cam_mask &= ~(1 << cam_index);
20942ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
20952ca5b659SJoost Mulders 				    VR_CAM_CTRL_ENABLE);
20962ca5b659SJoost Mulders 				VR_PUT32(vrp->acc_reg, VR_CAM_MASK, cam_mask);
20972ca5b659SJoost Mulders 				VR_PUT8(vrp->acc_reg, VR_CAM_CTRL,
20982ca5b659SJoost Mulders 				    VR_CAM_CTRL_DONE);
20992ca5b659SJoost Mulders 			} else {
21002ca5b659SJoost Mulders 				/*
21012ca5b659SJoost Mulders 				 * The entry to be removed was not found
21022ca5b659SJoost Mulders 				 * The likely cause is that the CAM was full
21032ca5b659SJoost Mulders 				 * during addition. The entry is added to the
21042ca5b659SJoost Mulders 				 * hash filter in that case and needs to be
21052ca5b659SJoost Mulders 				 * removed there too.
21062ca5b659SJoost Mulders 				 */
21072ca5b659SJoost Mulders 				use_hash_filter = B_TRUE;
21082ca5b659SJoost Mulders 			}
21092ca5b659SJoost Mulders 		}
21102ca5b659SJoost Mulders 	} else {
21112ca5b659SJoost Mulders 		/*
21122ca5b659SJoost Mulders 		 * No CAM in the MAC, thus we need the hash filter.
21132ca5b659SJoost Mulders 		 */
21142ca5b659SJoost Mulders 		use_hash_filter = B_TRUE;
21152ca5b659SJoost Mulders 	}
21162ca5b659SJoost Mulders 
21172ca5b659SJoost Mulders 	if (use_hash_filter == B_TRUE) {
21182ca5b659SJoost Mulders 		/*
21192ca5b659SJoost Mulders 		 * Get the CRC-32 of the multicast address
21202ca5b659SJoost Mulders 		 * The card uses the "MSB first" direction when calculating the
21212ca5b659SJoost Mulders 		 * the CRC. This is odd because ethernet is "LSB first"
21222ca5b659SJoost Mulders 		 * We have to use that "big endian" approach as well.
21232ca5b659SJoost Mulders 		 */
21242ca5b659SJoost Mulders 		crc_index = ether_crc_be(mca) >> (32 - 6);
21252ca5b659SJoost Mulders 		if (add == B_TRUE) {
21262ca5b659SJoost Mulders 			/*
21272ca5b659SJoost Mulders 			 * Turn bit[crc_index] on.
21282ca5b659SJoost Mulders 			 */
21292ca5b659SJoost Mulders 			if (crc_index < 32)
21302ca5b659SJoost Mulders 				vrp->mhash0 |= (1 << crc_index);
21312ca5b659SJoost Mulders 			else
21322ca5b659SJoost Mulders 				vrp->mhash1 |= (1 << (crc_index - 32));
21332ca5b659SJoost Mulders 		} else {
21342ca5b659SJoost Mulders 			/*
21352ca5b659SJoost Mulders 			 * Turn bit[crc_index] off.
21362ca5b659SJoost Mulders 			 */
21372ca5b659SJoost Mulders 			if (crc_index < 32)
21382ca5b659SJoost Mulders 				vrp->mhash0 &= ~(0 << crc_index);
21392ca5b659SJoost Mulders 			else
21402ca5b659SJoost Mulders 				vrp->mhash1 &= ~(0 << (crc_index - 32));
21412ca5b659SJoost Mulders 		}
21422ca5b659SJoost Mulders 
21432ca5b659SJoost Mulders 		/*
21442ca5b659SJoost Mulders 		 * When not promiscuous write the filter now. When promiscuous,
21452ca5b659SJoost Mulders 		 * the filter is open and will be written when promiscuous ends.
21462ca5b659SJoost Mulders 		 */
21472ca5b659SJoost Mulders 		if (vrp->promisc == B_FALSE) {
21482ca5b659SJoost Mulders 			VR_PUT32(vrp->acc_reg, VR_MAR0, vrp->mhash0);
21492ca5b659SJoost Mulders 			VR_PUT32(vrp->acc_reg, VR_MAR1, vrp->mhash1);
21502ca5b659SJoost Mulders 		}
21512ca5b659SJoost Mulders 	}
21522ca5b659SJoost Mulders 
21532ca5b659SJoost Mulders 	/*
21542ca5b659SJoost Mulders 	 * Enable/disable multicast receivements based on mcount.
21552ca5b659SJoost Mulders 	 */
21562ca5b659SJoost Mulders 	if (add == B_TRUE)
21572ca5b659SJoost Mulders 		vrp->mcount++;
21582ca5b659SJoost Mulders 	else if (vrp->mcount != 0)
21592ca5b659SJoost Mulders 		vrp->mcount --;
21602ca5b659SJoost Mulders 	if (vrp->mcount != 0)
21612ca5b659SJoost Mulders 		VR_SETBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTMULTI);
21622ca5b659SJoost Mulders 	else
21632ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg, VR_RXCFG, VR_RXCFG_ACCEPTMULTI);
21642ca5b659SJoost Mulders 
21652ca5b659SJoost Mulders 	mutex_exit(&vrp->intrlock);
21662ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
21672ca5b659SJoost Mulders 	return (0);
21682ca5b659SJoost Mulders }
21692ca5b659SJoost Mulders 
21702ca5b659SJoost Mulders /*
21712ca5b659SJoost Mulders  * Calculate the CRC32 for 6 bytes of multicast address in MSB(it) first order.
21722ca5b659SJoost Mulders  * The MSB first order is a bit odd because Ethernet standard is LSB first
21732ca5b659SJoost Mulders  */
21742ca5b659SJoost Mulders static uint32_t
ether_crc_be(const uint8_t * data)21752ca5b659SJoost Mulders ether_crc_be(const uint8_t *data)
21762ca5b659SJoost Mulders {
21772ca5b659SJoost Mulders 	uint32_t	crc = (uint32_t)0xFFFFFFFFU;
21782ca5b659SJoost Mulders 	uint32_t	carry;
21792ca5b659SJoost Mulders 	uint32_t	bit;
21802ca5b659SJoost Mulders 	uint32_t	length;
21812ca5b659SJoost Mulders 	uint8_t		c;
21822ca5b659SJoost Mulders 
21832ca5b659SJoost Mulders 	for (length = 0; length < ETHERADDRL; length++) {
21842ca5b659SJoost Mulders 		c = data[length];
21852ca5b659SJoost Mulders 		for (bit = 0; bit < 8; bit++) {
21862ca5b659SJoost Mulders 			carry = ((crc & 0x80000000U) ? 1 : 0) ^ (c & 0x01);
21872ca5b659SJoost Mulders 			crc <<= 1;
21882ca5b659SJoost Mulders 			c >>= 1;
21892ca5b659SJoost Mulders 			if (carry)
21902ca5b659SJoost Mulders 				crc = (crc ^ 0x04C11DB6) | carry;
21912ca5b659SJoost Mulders 		}
21922ca5b659SJoost Mulders 	}
21932ca5b659SJoost Mulders 	return (crc);
21942ca5b659SJoost Mulders }
21952ca5b659SJoost Mulders 
21962ca5b659SJoost Mulders 
21972ca5b659SJoost Mulders /*
21982ca5b659SJoost Mulders  * Return the CAM index (base 0) of maddr or -1 if maddr is not found
21992ca5b659SJoost Mulders  * If maddr is 0, return the index of an empty slot in CAM or -1 when no free
22002ca5b659SJoost Mulders  * slots available.
22012ca5b659SJoost Mulders  */
22022ca5b659SJoost Mulders static int32_t
vr_cam_index(vr_t * vrp,const uint8_t * maddr)22032ca5b659SJoost Mulders vr_cam_index(vr_t *vrp, const uint8_t *maddr)
22042ca5b659SJoost Mulders {
22052ca5b659SJoost Mulders 	ether_addr_t	taddr;
22062ca5b659SJoost Mulders 	int32_t		index;
22072ca5b659SJoost Mulders 	uint32_t	mask;
22082ca5b659SJoost Mulders 	uint32_t	a;
22092ca5b659SJoost Mulders 
22102ca5b659SJoost Mulders 	bzero(&taddr, sizeof (taddr));
22112ca5b659SJoost Mulders 
22122ca5b659SJoost Mulders 	/*
22132ca5b659SJoost Mulders 	 * Read the CAM mask from the controller.
22142ca5b659SJoost Mulders 	 */
22152ca5b659SJoost Mulders 	mask = VR_GET32(vrp->acc_reg, VR_CAM_MASK);
22162ca5b659SJoost Mulders 
22172ca5b659SJoost Mulders 	/*
22182ca5b659SJoost Mulders 	 * If maddr is 0, return the first unused slot or -1 for no unused.
22192ca5b659SJoost Mulders 	 */
22202ca5b659SJoost Mulders 	if (bcmp(maddr, taddr, ETHERADDRL) == 0) {
22212ca5b659SJoost Mulders 		/*
22222ca5b659SJoost Mulders 		 * Look for the first unused position in mask.
22232ca5b659SJoost Mulders 		 */
22242ca5b659SJoost Mulders 		for (index = 0; index < VR_CAM_SZ; index++) {
22252ca5b659SJoost Mulders 			if (((mask >> index) & 1) == 0)
22262ca5b659SJoost Mulders 				return (index);
22272ca5b659SJoost Mulders 		}
22282ca5b659SJoost Mulders 		return (-1);
22292ca5b659SJoost Mulders 	} else {
22302ca5b659SJoost Mulders 		/*
22312ca5b659SJoost Mulders 		 * Look for maddr in CAM.
22322ca5b659SJoost Mulders 		 */
22332ca5b659SJoost Mulders 		for (index = 0; index < VR_CAM_SZ; index++) {
22342ca5b659SJoost Mulders 			/* Look at enabled entries only */
22352ca5b659SJoost Mulders 			if (((mask >> index) & 1) == 0)
22362ca5b659SJoost Mulders 				continue;
22372ca5b659SJoost Mulders 
22382ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_ENABLE);
22392ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_CAM_ADDR, index);
22402ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_READ);
22412ca5b659SJoost Mulders 			drv_usecwait(2);
22422ca5b659SJoost Mulders 			for (a = 0; a < ETHERADDRL; a++)
22432ca5b659SJoost Mulders 				taddr[a] = VR_GET8(vrp->acc_reg, VR_MCAM0 + a);
22442ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_CAM_CTRL, VR_CAM_CTRL_DONE);
22452ca5b659SJoost Mulders 			if (bcmp(maddr, taddr, ETHERADDRL) == 0)
22462ca5b659SJoost Mulders 				return (index);
22472ca5b659SJoost Mulders 		}
22482ca5b659SJoost Mulders 	}
22492ca5b659SJoost Mulders 	return (-1);
22502ca5b659SJoost Mulders }
22512ca5b659SJoost Mulders 
22522ca5b659SJoost Mulders /*
22532ca5b659SJoost Mulders  * Set promiscuous mode on or off.
22542ca5b659SJoost Mulders  */
22552ca5b659SJoost Mulders int
vr_mac_set_promisc(void * p,boolean_t promiscflag)22562ca5b659SJoost Mulders vr_mac_set_promisc(void *p, boolean_t promiscflag)
22572ca5b659SJoost Mulders {
22582ca5b659SJoost Mulders 	vr_t		*vrp;
22592ca5b659SJoost Mulders 	uint8_t		rxcfg;
22602ca5b659SJoost Mulders 
22612ca5b659SJoost Mulders 	vrp = (vr_t *)p;
22622ca5b659SJoost Mulders 
22632ca5b659SJoost Mulders 	mutex_enter(&vrp->intrlock);
22642ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
22652ca5b659SJoost Mulders 	mutex_enter(&vrp->tx.lock);
22662ca5b659SJoost Mulders 
22672ca5b659SJoost Mulders 	/*
22682ca5b659SJoost Mulders 	 * Get current receive configuration.
22692ca5b659SJoost Mulders 	 */
22702ca5b659SJoost Mulders 	rxcfg = VR_GET8(vrp->acc_reg, VR_RXCFG);
22712ca5b659SJoost Mulders 	vrp->promisc = promiscflag;
22722ca5b659SJoost Mulders 
22732ca5b659SJoost Mulders 	if (promiscflag == B_TRUE) {
22742ca5b659SJoost Mulders 		/*
22752ca5b659SJoost Mulders 		 * Enable promiscuous mode and open the multicast filter.
22762ca5b659SJoost Mulders 		 */
22772ca5b659SJoost Mulders 		rxcfg |= (VR_RXCFG_PROMISC | VR_RXCFG_ACCEPTMULTI);
22782ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_MAR0, 0xffffffff);
22792ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_MAR1, 0xffffffff);
22802ca5b659SJoost Mulders 	} else {
22812ca5b659SJoost Mulders 		/*
22822ca5b659SJoost Mulders 		 * Restore the multicast filter and disable promiscuous mode.
22832ca5b659SJoost Mulders 		 */
22842ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_MAR0, vrp->mhash0);
22852ca5b659SJoost Mulders 		VR_PUT32(vrp->acc_reg, VR_MAR1, vrp->mhash1);
22862ca5b659SJoost Mulders 		rxcfg &= ~VR_RXCFG_PROMISC;
22872ca5b659SJoost Mulders 		if (vrp->mcount != 0)
22882ca5b659SJoost Mulders 			rxcfg |= VR_RXCFG_ACCEPTMULTI;
22892ca5b659SJoost Mulders 	}
22902ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_RXCFG, rxcfg);
22912ca5b659SJoost Mulders 	mutex_exit(&vrp->tx.lock);
22922ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
22932ca5b659SJoost Mulders 	mutex_exit(&vrp->intrlock);
22942ca5b659SJoost Mulders 	return (0);
22952ca5b659SJoost Mulders }
22962ca5b659SJoost Mulders 
22972ca5b659SJoost Mulders int
vr_mac_getstat(void * arg,uint_t stat,uint64_t * val)22982ca5b659SJoost Mulders vr_mac_getstat(void *arg, uint_t stat, uint64_t *val)
22992ca5b659SJoost Mulders {
23002ca5b659SJoost Mulders 	vr_t		*vrp;
23012ca5b659SJoost Mulders 	uint64_t	v;
23022ca5b659SJoost Mulders 
23032ca5b659SJoost Mulders 	vrp = (void *) arg;
23042ca5b659SJoost Mulders 
23052ca5b659SJoost Mulders 	switch (stat) {
23062ca5b659SJoost Mulders 	default:
23072ca5b659SJoost Mulders 		return (ENOTSUP);
23082ca5b659SJoost Mulders 
23092ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_100T4:
23102ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_T4) != 0;
23112ca5b659SJoost Mulders 		break;
23122ca5b659SJoost Mulders 
23132ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_100FDX:
23142ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_TX_FD) != 0;
23152ca5b659SJoost Mulders 		break;
23162ca5b659SJoost Mulders 
23172ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_100HDX:
23182ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_ABILITY_100BASE_TX) != 0;
23192ca5b659SJoost Mulders 		break;
23202ca5b659SJoost Mulders 
23212ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_10FDX:
23222ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_ABILITY_10BASE_T_FD) != 0;
23232ca5b659SJoost Mulders 		break;
23242ca5b659SJoost Mulders 
23252ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_10HDX:
23262ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_ABILITY_10BASE_T) != 0;
23272ca5b659SJoost Mulders 		break;
23282ca5b659SJoost Mulders 
23292ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
23302ca5b659SJoost Mulders 		v = 0;
23312ca5b659SJoost Mulders 		break;
23322ca5b659SJoost Mulders 
23332ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_AUTONEG:
23342ca5b659SJoost Mulders 		v = (vrp->chip.mii.control & MII_CONTROL_ANE) != 0;
23352ca5b659SJoost Mulders 		break;
23362ca5b659SJoost Mulders 
23372ca5b659SJoost Mulders 	case ETHER_STAT_ADV_CAP_PAUSE:
2338bdb9230aSGarrett D'Amore 		v = (vrp->chip.mii.anadv & MII_ABILITY_PAUSE) != 0;
23392ca5b659SJoost Mulders 		break;
23402ca5b659SJoost Mulders 
23412ca5b659SJoost Mulders 	case ETHER_STAT_ADV_REMFAULT:
23422ca5b659SJoost Mulders 		v = (vrp->chip.mii.anadv & MII_AN_ADVERT_REMFAULT) != 0;
23432ca5b659SJoost Mulders 		break;
23442ca5b659SJoost Mulders 
23452ca5b659SJoost Mulders 	case ETHER_STAT_ALIGN_ERRORS:
23462ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_align_errors;
23472ca5b659SJoost Mulders 		break;
23482ca5b659SJoost Mulders 
23492ca5b659SJoost Mulders 	case ETHER_STAT_CAP_100T4:
23502ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_100_BASE_T4) != 0;
23512ca5b659SJoost Mulders 		break;
23522ca5b659SJoost Mulders 
23532ca5b659SJoost Mulders 	case ETHER_STAT_CAP_100FDX:
23542ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_100_BASEX_FD) != 0;
23552ca5b659SJoost Mulders 		break;
23562ca5b659SJoost Mulders 
23572ca5b659SJoost Mulders 	case ETHER_STAT_CAP_100HDX:
23582ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_100_BASEX) != 0;
23592ca5b659SJoost Mulders 		break;
23602ca5b659SJoost Mulders 
23612ca5b659SJoost Mulders 	case ETHER_STAT_CAP_10FDX:
23622ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_10_FD) != 0;
23632ca5b659SJoost Mulders 		break;
23642ca5b659SJoost Mulders 
23652ca5b659SJoost Mulders 	case ETHER_STAT_CAP_10HDX:
23662ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_10) != 0;
23672ca5b659SJoost Mulders 		break;
23682ca5b659SJoost Mulders 
23692ca5b659SJoost Mulders 	case ETHER_STAT_CAP_ASMPAUSE:
23702ca5b659SJoost Mulders 		v = 0;
23712ca5b659SJoost Mulders 		break;
23722ca5b659SJoost Mulders 
23732ca5b659SJoost Mulders 	case ETHER_STAT_CAP_AUTONEG:
23742ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_CANAUTONEG) != 0;
23752ca5b659SJoost Mulders 		break;
23762ca5b659SJoost Mulders 
23772ca5b659SJoost Mulders 	case ETHER_STAT_CAP_PAUSE:
23782ca5b659SJoost Mulders 		v = 1;
23792ca5b659SJoost Mulders 		break;
23802ca5b659SJoost Mulders 
23812ca5b659SJoost Mulders 	case ETHER_STAT_CAP_REMFAULT:
23822ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0;
23832ca5b659SJoost Mulders 		break;
23842ca5b659SJoost Mulders 
23852ca5b659SJoost Mulders 	case ETHER_STAT_CARRIER_ERRORS:
23862ca5b659SJoost Mulders 		/*
23872ca5b659SJoost Mulders 		 * Number of times carrier was lost or never detected on a
23882ca5b659SJoost Mulders 		 * transmission attempt.
23892ca5b659SJoost Mulders 		 */
23902ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_carrier_errors;
23912ca5b659SJoost Mulders 		break;
23922ca5b659SJoost Mulders 
23932ca5b659SJoost Mulders 	case ETHER_STAT_JABBER_ERRORS:
23942ca5b659SJoost Mulders 		return (ENOTSUP);
23952ca5b659SJoost Mulders 
23962ca5b659SJoost Mulders 	case ETHER_STAT_DEFER_XMTS:
23972ca5b659SJoost Mulders 		/*
23982ca5b659SJoost Mulders 		 * Packets without collisions where first transmit attempt was
23992ca5b659SJoost Mulders 		 * delayed because the medium was busy.
24002ca5b659SJoost Mulders 		 */
24012ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_defer_xmts;
24022ca5b659SJoost Mulders 		break;
24032ca5b659SJoost Mulders 
24042ca5b659SJoost Mulders 	case ETHER_STAT_EX_COLLISIONS:
24052ca5b659SJoost Mulders 		/*
24062ca5b659SJoost Mulders 		 * Frames where excess collisions occurred on transmit, causing
24072ca5b659SJoost Mulders 		 * transmit failure.
24082ca5b659SJoost Mulders 		 */
24092ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_ex_collisions;
24102ca5b659SJoost Mulders 		break;
24112ca5b659SJoost Mulders 
24122ca5b659SJoost Mulders 	case ETHER_STAT_FCS_ERRORS:
24132ca5b659SJoost Mulders 		/*
24142ca5b659SJoost Mulders 		 * Packets received with CRC errors.
24152ca5b659SJoost Mulders 		 */
24162ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_fcs_errors;
24172ca5b659SJoost Mulders 		break;
24182ca5b659SJoost Mulders 
24192ca5b659SJoost Mulders 	case ETHER_STAT_FIRST_COLLISIONS:
24202ca5b659SJoost Mulders 		/*
24212ca5b659SJoost Mulders 		 * Packets successfully transmitted with exactly one collision.
24222ca5b659SJoost Mulders 		 */
24232ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_first_collisions;
24242ca5b659SJoost Mulders 		break;
24252ca5b659SJoost Mulders 
24262ca5b659SJoost Mulders 	case ETHER_STAT_LINK_ASMPAUSE:
24272ca5b659SJoost Mulders 		v = 0;
24282ca5b659SJoost Mulders 		break;
24292ca5b659SJoost Mulders 
24302ca5b659SJoost Mulders 	case ETHER_STAT_LINK_AUTONEG:
24312ca5b659SJoost Mulders 		v = (vrp->chip.mii.control & MII_CONTROL_ANE) != 0 &&
24322ca5b659SJoost Mulders 		    (vrp->chip.mii.status & MII_STATUS_ANDONE) != 0;
24332ca5b659SJoost Mulders 		break;
24342ca5b659SJoost Mulders 
24352ca5b659SJoost Mulders 	case ETHER_STAT_LINK_DUPLEX:
24362ca5b659SJoost Mulders 		v = vrp->chip.link.duplex;
24372ca5b659SJoost Mulders 		break;
24382ca5b659SJoost Mulders 
24392ca5b659SJoost Mulders 	case ETHER_STAT_LINK_PAUSE:
24402ca5b659SJoost Mulders 		v = vrp->chip.link.flowctrl;
24412ca5b659SJoost Mulders 		break;
24422ca5b659SJoost Mulders 
24432ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_100T4:
24442ca5b659SJoost Mulders 		v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_T4) != 0;
24452ca5b659SJoost Mulders 		break;
24462ca5b659SJoost Mulders 
24472ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_1000FDX:
24482ca5b659SJoost Mulders 		v = 0;
24492ca5b659SJoost Mulders 		break;
24502ca5b659SJoost Mulders 
24512ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_1000HDX:
24522ca5b659SJoost Mulders 		v = 0;
24532ca5b659SJoost Mulders 		break;
24542ca5b659SJoost Mulders 
24552ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_100FDX:
24562ca5b659SJoost Mulders 		v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_TX_FD) != 0;
24572ca5b659SJoost Mulders 		break;
24582ca5b659SJoost Mulders 
24592ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_100HDX:
24602ca5b659SJoost Mulders 		v = (vrp->chip.mii.lpable & MII_ABILITY_100BASE_TX) != 0;
24612ca5b659SJoost Mulders 		break;
24622ca5b659SJoost Mulders 
24632ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_10FDX:
24642ca5b659SJoost Mulders 		v = (vrp->chip.mii.lpable & MII_ABILITY_10BASE_T_FD) != 0;
24652ca5b659SJoost Mulders 		break;
24662ca5b659SJoost Mulders 
24672ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_10HDX:
24682ca5b659SJoost Mulders 		v = (vrp->chip.mii.lpable & MII_ABILITY_10BASE_T) != 0;
24692ca5b659SJoost Mulders 		break;
24702ca5b659SJoost Mulders 
24712ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_ASMPAUSE:
24722ca5b659SJoost Mulders 		v = 0;
24732ca5b659SJoost Mulders 		break;
24742ca5b659SJoost Mulders 
24752ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_AUTONEG:
24762ca5b659SJoost Mulders 		v = (vrp->chip.mii.anexp & MII_AN_EXP_LPCANAN) != 0;
24772ca5b659SJoost Mulders 		break;
24782ca5b659SJoost Mulders 
24792ca5b659SJoost Mulders 	case ETHER_STAT_LP_CAP_PAUSE:
2480bdb9230aSGarrett D'Amore 		v = (vrp->chip.mii.lpable & MII_ABILITY_PAUSE) != 0;
24812ca5b659SJoost Mulders 		break;
24822ca5b659SJoost Mulders 
24832ca5b659SJoost Mulders 	case ETHER_STAT_LP_REMFAULT:
24842ca5b659SJoost Mulders 		v = (vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0;
24852ca5b659SJoost Mulders 		break;
24862ca5b659SJoost Mulders 
24872ca5b659SJoost Mulders 	case ETHER_STAT_MACRCV_ERRORS:
24882ca5b659SJoost Mulders 		/*
24892ca5b659SJoost Mulders 		 * Packets received with MAC errors, except align_errors,
24902ca5b659SJoost Mulders 		 * fcs_errors, and toolong_errors.
24912ca5b659SJoost Mulders 		 */
24922ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_macrcv_errors;
24932ca5b659SJoost Mulders 		break;
24942ca5b659SJoost Mulders 
24952ca5b659SJoost Mulders 	case ETHER_STAT_MACXMT_ERRORS:
24962ca5b659SJoost Mulders 		/*
24972ca5b659SJoost Mulders 		 * Packets encountering transmit MAC failures, except carrier
24982ca5b659SJoost Mulders 		 * and collision failures.
24992ca5b659SJoost Mulders 		 */
25002ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_macxmt_errors;
25012ca5b659SJoost Mulders 		break;
25022ca5b659SJoost Mulders 
25032ca5b659SJoost Mulders 	case ETHER_STAT_MULTI_COLLISIONS:
25042ca5b659SJoost Mulders 		/*
25052ca5b659SJoost Mulders 		 * Packets successfully transmitted with multiple collisions.
25062ca5b659SJoost Mulders 		 */
25072ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_multi_collisions;
25082ca5b659SJoost Mulders 		break;
25092ca5b659SJoost Mulders 
25102ca5b659SJoost Mulders 	case ETHER_STAT_SQE_ERRORS:
25112ca5b659SJoost Mulders 		/*
25122ca5b659SJoost Mulders 		 * Number of times signal quality error was reported
25132ca5b659SJoost Mulders 		 * This one is reported by the PHY.
25142ca5b659SJoost Mulders 		 */
25152ca5b659SJoost Mulders 		return (ENOTSUP);
25162ca5b659SJoost Mulders 
25172ca5b659SJoost Mulders 	case ETHER_STAT_TOOLONG_ERRORS:
25182ca5b659SJoost Mulders 		/*
25192ca5b659SJoost Mulders 		 * Packets received larger than the maximum permitted length.
25202ca5b659SJoost Mulders 		 */
25212ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_toolong_errors;
25222ca5b659SJoost Mulders 		break;
25232ca5b659SJoost Mulders 
25242ca5b659SJoost Mulders 	case ETHER_STAT_TOOSHORT_ERRORS:
25252ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_tooshort_errors;
25262ca5b659SJoost Mulders 		break;
25272ca5b659SJoost Mulders 
25282ca5b659SJoost Mulders 	case ETHER_STAT_TX_LATE_COLLISIONS:
25292ca5b659SJoost Mulders 		/*
25302ca5b659SJoost Mulders 		 * Number of times a transmit collision occurred late
25312ca5b659SJoost Mulders 		 * (after 512 bit times).
25322ca5b659SJoost Mulders 		 */
25332ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_tx_late_collisions;
25342ca5b659SJoost Mulders 		break;
25352ca5b659SJoost Mulders 
25362ca5b659SJoost Mulders 	case ETHER_STAT_XCVR_ADDR:
25372ca5b659SJoost Mulders 		/*
25382ca5b659SJoost Mulders 		 * MII address in the 0 to 31 range of the physical layer
25392ca5b659SJoost Mulders 		 * device in use for a given Ethernet device.
25402ca5b659SJoost Mulders 		 */
25412ca5b659SJoost Mulders 		v = vrp->chip.phyaddr;
25422ca5b659SJoost Mulders 		break;
25432ca5b659SJoost Mulders 
25442ca5b659SJoost Mulders 	case ETHER_STAT_XCVR_ID:
25452ca5b659SJoost Mulders 		/*
25462ca5b659SJoost Mulders 		 * MII transceiver manufacturer and device ID.
25472ca5b659SJoost Mulders 		 */
25482ca5b659SJoost Mulders 		v = (vrp->chip.mii.identh << 16) | vrp->chip.mii.identl;
25492ca5b659SJoost Mulders 		break;
25502ca5b659SJoost Mulders 
25512ca5b659SJoost Mulders 	case ETHER_STAT_XCVR_INUSE:
25522ca5b659SJoost Mulders 		v = vrp->chip.link.mau;
25532ca5b659SJoost Mulders 		break;
25542ca5b659SJoost Mulders 
25552ca5b659SJoost Mulders 	case MAC_STAT_BRDCSTRCV:
25562ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_brdcstrcv;
25572ca5b659SJoost Mulders 		break;
25582ca5b659SJoost Mulders 
25592ca5b659SJoost Mulders 	case MAC_STAT_BRDCSTXMT:
25602ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_brdcstxmt;
25612ca5b659SJoost Mulders 		break;
25622ca5b659SJoost Mulders 
25632ca5b659SJoost Mulders 	case MAC_STAT_MULTIXMT:
25642ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_multixmt;
25652ca5b659SJoost Mulders 		break;
25662ca5b659SJoost Mulders 
25672ca5b659SJoost Mulders 	case MAC_STAT_COLLISIONS:
25682ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_collisions;
25692ca5b659SJoost Mulders 		break;
25702ca5b659SJoost Mulders 
25712ca5b659SJoost Mulders 	case MAC_STAT_IERRORS:
25722ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_ierrors;
25732ca5b659SJoost Mulders 		break;
25742ca5b659SJoost Mulders 
25752ca5b659SJoost Mulders 	case MAC_STAT_IFSPEED:
25762ca5b659SJoost Mulders 		if (vrp->chip.link.speed == VR_LINK_SPEED_100MBS)
25772ca5b659SJoost Mulders 			v = 100 * 1000 * 1000;
25782ca5b659SJoost Mulders 		else if (vrp->chip.link.speed == VR_LINK_SPEED_10MBS)
25792ca5b659SJoost Mulders 			v = 10 * 1000 * 1000;
25802ca5b659SJoost Mulders 		else
25812ca5b659SJoost Mulders 			v = 0;
25822ca5b659SJoost Mulders 		break;
25832ca5b659SJoost Mulders 
25842ca5b659SJoost Mulders 	case MAC_STAT_IPACKETS:
25852ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_ipackets;
25862ca5b659SJoost Mulders 		break;
25872ca5b659SJoost Mulders 
25882ca5b659SJoost Mulders 	case MAC_STAT_MULTIRCV:
25892ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_multircv;
25902ca5b659SJoost Mulders 		break;
25912ca5b659SJoost Mulders 
25922ca5b659SJoost Mulders 	case MAC_STAT_NORCVBUF:
25932ca5b659SJoost Mulders 		vrp->stats.mac_stat_norcvbuf +=
25942ca5b659SJoost Mulders 		    VR_GET16(vrp->acc_reg, VR_TALLY_MPA);
25952ca5b659SJoost Mulders 		VR_PUT16(vrp->acc_reg, VR_TALLY_MPA, 0);
25962ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_norcvbuf;
25972ca5b659SJoost Mulders 		break;
25982ca5b659SJoost Mulders 
25992ca5b659SJoost Mulders 	case MAC_STAT_NOXMTBUF:
26002ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_noxmtbuf;
26012ca5b659SJoost Mulders 		break;
26022ca5b659SJoost Mulders 
26032ca5b659SJoost Mulders 	case MAC_STAT_OBYTES:
26042ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_obytes;
26052ca5b659SJoost Mulders 		break;
26062ca5b659SJoost Mulders 
26072ca5b659SJoost Mulders 	case MAC_STAT_OERRORS:
26082ca5b659SJoost Mulders 		v = vrp->stats.ether_stat_macxmt_errors +
26092ca5b659SJoost Mulders 		    vrp->stats.mac_stat_underflows +
26102ca5b659SJoost Mulders 		    vrp->stats.ether_stat_align_errors +
26112ca5b659SJoost Mulders 		    vrp->stats.ether_stat_carrier_errors +
26122ca5b659SJoost Mulders 		    vrp->stats.ether_stat_fcs_errors;
26132ca5b659SJoost Mulders 		break;
26142ca5b659SJoost Mulders 
26152ca5b659SJoost Mulders 	case MAC_STAT_OPACKETS:
26162ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_opackets;
26172ca5b659SJoost Mulders 		break;
26182ca5b659SJoost Mulders 
26192ca5b659SJoost Mulders 	case MAC_STAT_RBYTES:
26202ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_rbytes;
26212ca5b659SJoost Mulders 		break;
26222ca5b659SJoost Mulders 
26232ca5b659SJoost Mulders 	case MAC_STAT_UNKNOWNS:
26242ca5b659SJoost Mulders 		/*
26252ca5b659SJoost Mulders 		 * Isn't this something for the MAC layer to maintain?
26262ca5b659SJoost Mulders 		 */
26272ca5b659SJoost Mulders 		return (ENOTSUP);
26282ca5b659SJoost Mulders 
26292ca5b659SJoost Mulders 	case MAC_STAT_UNDERFLOWS:
26302ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_underflows;
26312ca5b659SJoost Mulders 		break;
26322ca5b659SJoost Mulders 
26332ca5b659SJoost Mulders 	case MAC_STAT_OVERFLOWS:
26342ca5b659SJoost Mulders 		v = vrp->stats.mac_stat_overflows;
26352ca5b659SJoost Mulders 		break;
26362ca5b659SJoost Mulders 	}
26372ca5b659SJoost Mulders 	*val = v;
26382ca5b659SJoost Mulders 	return (0);
26392ca5b659SJoost Mulders }
26402ca5b659SJoost Mulders 
26412ca5b659SJoost Mulders int
vr_mac_set_ether_addr(void * p,const uint8_t * ea)26422ca5b659SJoost Mulders vr_mac_set_ether_addr(void *p, const uint8_t *ea)
26432ca5b659SJoost Mulders {
26442ca5b659SJoost Mulders 	vr_t	*vrp;
26452ca5b659SJoost Mulders 	int	i;
26462ca5b659SJoost Mulders 
26472ca5b659SJoost Mulders 	vrp = (vr_t *)p;
26482ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
26492ca5b659SJoost Mulders 	mutex_enter(&vrp->intrlock);
26502ca5b659SJoost Mulders 
26512ca5b659SJoost Mulders 	/*
26522ca5b659SJoost Mulders 	 * Set a new station address.
26532ca5b659SJoost Mulders 	 */
26542ca5b659SJoost Mulders 	for (i = 0; i < ETHERADDRL; i++)
26552ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_ETHERADDR + i, ea[i]);
26562ca5b659SJoost Mulders 
26572ca5b659SJoost Mulders 	mutex_exit(&vrp->intrlock);
26582ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
26592ca5b659SJoost Mulders 	return (0);
26602ca5b659SJoost Mulders }
26612ca5b659SJoost Mulders 
26622ca5b659SJoost Mulders /*
26632ca5b659SJoost Mulders  * Configure the ethernet link according to param and chip.mii.
26642ca5b659SJoost Mulders  */
26652ca5b659SJoost Mulders static void
vr_link_init(vr_t * vrp)26662ca5b659SJoost Mulders vr_link_init(vr_t *vrp)
26672ca5b659SJoost Mulders {
26682ca5b659SJoost Mulders 	ASSERT(mutex_owned(&vrp->oplock));
26692ca5b659SJoost Mulders 	if ((vrp->chip.mii.control & MII_CONTROL_ANE) != 0) {
26702ca5b659SJoost Mulders 		/*
26712ca5b659SJoost Mulders 		 * If we do autoneg, ensure restart autoneg is ON.
26722ca5b659SJoost Mulders 		 */
26732ca5b659SJoost Mulders 		vrp->chip.mii.control |= MII_CONTROL_RSAN;
26742ca5b659SJoost Mulders 
26752ca5b659SJoost Mulders 		/*
26762ca5b659SJoost Mulders 		 * The advertisements are prepared by param_init.
26772ca5b659SJoost Mulders 		 */
26782ca5b659SJoost Mulders 		vr_phy_write(vrp, MII_AN_ADVERT, vrp->chip.mii.anadv);
26792ca5b659SJoost Mulders 	} else {
26802ca5b659SJoost Mulders 		/*
26812ca5b659SJoost Mulders 		 * If we don't autoneg, we need speed, duplex and flowcontrol
26822ca5b659SJoost Mulders 		 * to configure the link. However, dladm doesn't allow changes
26832ca5b659SJoost Mulders 		 * to speed and duplex (readonly). The way this is solved
26842ca5b659SJoost Mulders 		 * (ahem) is to select the highest enabled combination
26852ca5b659SJoost Mulders 		 * Speed and duplex should be r/w when autoneg is off.
26862ca5b659SJoost Mulders 		 */
26872ca5b659SJoost Mulders 		if ((vrp->param.anadv_en &
26882ca5b659SJoost Mulders 		    MII_ABILITY_100BASE_TX_FD) != 0) {
26892ca5b659SJoost Mulders 			vrp->chip.mii.control |= MII_CONTROL_100MB;
26902ca5b659SJoost Mulders 			vrp->chip.mii.control |= MII_CONTROL_FDUPLEX;
26912ca5b659SJoost Mulders 		} else if ((vrp->param.anadv_en &
26922ca5b659SJoost Mulders 		    MII_ABILITY_100BASE_TX) != 0) {
26932ca5b659SJoost Mulders 			vrp->chip.mii.control |= MII_CONTROL_100MB;
26942ca5b659SJoost Mulders 			vrp->chip.mii.control &= ~MII_CONTROL_FDUPLEX;
26952ca5b659SJoost Mulders 		} else if ((vrp->param.anadv_en &
26962ca5b659SJoost Mulders 		    MII_ABILITY_10BASE_T_FD) != 0) {
26972ca5b659SJoost Mulders 			vrp->chip.mii.control |= MII_CONTROL_FDUPLEX;
26982ca5b659SJoost Mulders 			vrp->chip.mii.control &= ~MII_CONTROL_100MB;
26992ca5b659SJoost Mulders 		} else {
27002ca5b659SJoost Mulders 			vrp->chip.mii.control &= ~MII_CONTROL_100MB;
27012ca5b659SJoost Mulders 			vrp->chip.mii.control &= ~MII_CONTROL_FDUPLEX;
27022ca5b659SJoost Mulders 		}
27032ca5b659SJoost Mulders 	}
27042ca5b659SJoost Mulders 	/*
27052ca5b659SJoost Mulders 	 * Write the control register.
27062ca5b659SJoost Mulders 	 */
27072ca5b659SJoost Mulders 	vr_phy_write(vrp, MII_CONTROL, vrp->chip.mii.control);
27082ca5b659SJoost Mulders 
27092ca5b659SJoost Mulders 	/*
27102ca5b659SJoost Mulders 	 * With autoneg off we cannot rely on the link_change interrupt for
27112ca5b659SJoost Mulders 	 * for getting the status into the driver.
27122ca5b659SJoost Mulders 	 */
27132ca5b659SJoost Mulders 	if ((vrp->chip.mii.control & MII_CONTROL_ANE) == 0) {
27142ca5b659SJoost Mulders 		vr_link_state(vrp);
27152ca5b659SJoost Mulders 		mac_link_update(vrp->machdl,
27162ca5b659SJoost Mulders 		    (link_state_t)vrp->chip.link.state);
27172ca5b659SJoost Mulders 	}
27182ca5b659SJoost Mulders }
27192ca5b659SJoost Mulders 
27202ca5b659SJoost Mulders /*
27212ca5b659SJoost Mulders  * Get link state in the driver and configure the MAC accordingly.
27222ca5b659SJoost Mulders  */
27232ca5b659SJoost Mulders static void
vr_link_state(vr_t * vrp)27242ca5b659SJoost Mulders vr_link_state(vr_t *vrp)
27252ca5b659SJoost Mulders {
27262ca5b659SJoost Mulders 	uint16_t		mask;
27272ca5b659SJoost Mulders 
27282ca5b659SJoost Mulders 	ASSERT(mutex_owned(&vrp->oplock));
27292ca5b659SJoost Mulders 
27302ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_STATUS, &vrp->chip.mii.status);
27312ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_CONTROL, &vrp->chip.mii.control);
27322ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_AN_ADVERT, &vrp->chip.mii.anadv);
27332ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_AN_LPABLE, &vrp->chip.mii.lpable);
27342ca5b659SJoost Mulders 	vr_phy_read(vrp, MII_AN_EXPANSION, &vrp->chip.mii.anexp);
27352ca5b659SJoost Mulders 
27362ca5b659SJoost Mulders 	/*
27372ca5b659SJoost Mulders 	 * If we did autongeg, deduce the link type/speed by selecting the
27382ca5b659SJoost Mulders 	 * highest common denominator.
27392ca5b659SJoost Mulders 	 */
27402ca5b659SJoost Mulders 	if ((vrp->chip.mii.control & MII_CONTROL_ANE) != 0) {
27412ca5b659SJoost Mulders 		mask = vrp->chip.mii.anadv & vrp->chip.mii.lpable;
27422ca5b659SJoost Mulders 		if ((mask & MII_ABILITY_100BASE_TX_FD) != 0) {
27432ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
27442ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
27452ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_100X;
27462ca5b659SJoost Mulders 		} else if ((mask & MII_ABILITY_100BASE_T4) != 0) {
27472ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
27482ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
27492ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_100T4;
27502ca5b659SJoost Mulders 		} else if ((mask & MII_ABILITY_100BASE_TX) != 0) {
27512ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
27522ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
27532ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_100X;
27542ca5b659SJoost Mulders 		} else if ((mask & MII_ABILITY_10BASE_T_FD) != 0) {
27552ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
27562ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
27572ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_10;
27582ca5b659SJoost Mulders 		} else if ((mask & MII_ABILITY_10BASE_T) != 0) {
27592ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
27602ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
27612ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_10;
27622ca5b659SJoost Mulders 		} else {
27632ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_UNKNOWN;
27642ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_UNKNOWN;
27652ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_UNKNOWN;
27662ca5b659SJoost Mulders 		}
27672ca5b659SJoost Mulders 
27682ca5b659SJoost Mulders 		/*
27692ca5b659SJoost Mulders 		 * Did we negotiate pause?
27702ca5b659SJoost Mulders 		 */
2771bdb9230aSGarrett D'Amore 		if ((mask & MII_ABILITY_PAUSE) != 0 &&
27722ca5b659SJoost Mulders 		    vrp->chip.link.duplex == VR_LINK_DUPLEX_FULL)
27732ca5b659SJoost Mulders 			vrp->chip.link.flowctrl = VR_PAUSE_BIDIRECTIONAL;
27742ca5b659SJoost Mulders 		else
27752ca5b659SJoost Mulders 			vrp->chip.link.flowctrl = VR_PAUSE_NONE;
27762ca5b659SJoost Mulders 
27772ca5b659SJoost Mulders 		/*
27782ca5b659SJoost Mulders 		 * Did either one detect a AN fault?
27792ca5b659SJoost Mulders 		 */
27802ca5b659SJoost Mulders 		if ((vrp->chip.mii.status & MII_STATUS_REMFAULT) != 0)
27812ca5b659SJoost Mulders 			vr_log(vrp, CE_WARN,
27822ca5b659SJoost Mulders 			    "AN remote fault reported by LP.");
27832ca5b659SJoost Mulders 
27842ca5b659SJoost Mulders 		if ((vrp->chip.mii.lpable & MII_AN_ADVERT_REMFAULT) != 0)
27852ca5b659SJoost Mulders 			vr_log(vrp, CE_WARN, "AN remote fault caused for LP.");
27862ca5b659SJoost Mulders 	} else {
27872ca5b659SJoost Mulders 		/*
27882ca5b659SJoost Mulders 		 * We didn't autoneg
27892ca5b659SJoost Mulders 		 * The link type is defined by the control register.
27902ca5b659SJoost Mulders 		 */
27912ca5b659SJoost Mulders 		if ((vrp->chip.mii.control & MII_CONTROL_100MB) != 0) {
27922ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_100MBS;
27932ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_100X;
27942ca5b659SJoost Mulders 		} else {
27952ca5b659SJoost Mulders 			vrp->chip.link.speed = VR_LINK_SPEED_10MBS;
27962ca5b659SJoost Mulders 			vrp->chip.link.mau = VR_MAU_10;
27972ca5b659SJoost Mulders 		}
27982ca5b659SJoost Mulders 
27992ca5b659SJoost Mulders 		if ((vrp->chip.mii.control & MII_CONTROL_FDUPLEX) != 0)
28002ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_FULL;
28012ca5b659SJoost Mulders 		else {
28022ca5b659SJoost Mulders 			vrp->chip.link.duplex = VR_LINK_DUPLEX_HALF;
28032ca5b659SJoost Mulders 			/*
28042ca5b659SJoost Mulders 			 * No pause on HDX links.
28052ca5b659SJoost Mulders 			 */
28062ca5b659SJoost Mulders 			vrp->chip.link.flowctrl = VR_PAUSE_NONE;
28072ca5b659SJoost Mulders 		}
28082ca5b659SJoost Mulders 	}
28092ca5b659SJoost Mulders 
28102ca5b659SJoost Mulders 	/*
28112ca5b659SJoost Mulders 	 * Set the duplex mode on the MAC according to that of the PHY.
28122ca5b659SJoost Mulders 	 */
28132ca5b659SJoost Mulders 	if (vrp->chip.link.duplex == VR_LINK_DUPLEX_FULL) {
28142ca5b659SJoost Mulders 		VR_SETBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_MACFULLDUPLEX);
28152ca5b659SJoost Mulders 		/*
28162ca5b659SJoost Mulders 		 * Enable packet queueing on FDX links.
28172ca5b659SJoost Mulders 		 */
28182ca5b659SJoost Mulders 		if ((vrp->chip.info.bugs & VR_BUG_NO_TXQUEUEING) == 0)
28192ca5b659SJoost Mulders 			VR_CLRBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_QPKTDIS);
28202ca5b659SJoost Mulders 	} else {
28212ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg, VR_CTRL1, VR_CTRL1_MACFULLDUPLEX);
28222ca5b659SJoost Mulders 		/*
28232ca5b659SJoost Mulders 		 * Disable packet queueing on HDX links. With queueing enabled,
28242ca5b659SJoost Mulders 		 * this MAC get's lost after a TX abort (too many colisions).
28252ca5b659SJoost Mulders 		 */
28262ca5b659SJoost Mulders 		VR_SETBIT8(vrp->acc_reg, VR_CFGB, VR_CFGB_QPKTDIS);
28272ca5b659SJoost Mulders 	}
28282ca5b659SJoost Mulders 
28292ca5b659SJoost Mulders 	/*
28302ca5b659SJoost Mulders 	 * Set pause options on the MAC.
28312ca5b659SJoost Mulders 	 */
28322ca5b659SJoost Mulders 	if (vrp->chip.link.flowctrl == VR_PAUSE_BIDIRECTIONAL) {
28332ca5b659SJoost Mulders 		/*
28342ca5b659SJoost Mulders 		 * All of our MAC's can receive pause frames.
28352ca5b659SJoost Mulders 		 */
28362ca5b659SJoost Mulders 		VR_SETBIT8(vrp->acc_reg, VR_MISC0, VR_MISC0_FDXRFEN);
28372ca5b659SJoost Mulders 
28382ca5b659SJoost Mulders 		/*
28392ca5b659SJoost Mulders 		 * VT6105 and above can transmit pause frames.
28402ca5b659SJoost Mulders 		 */
28412ca5b659SJoost Mulders 		if ((vrp->chip.info.features & VR_FEATURE_TX_PAUSE_CAP) != 0) {
28422ca5b659SJoost Mulders 			/*
28432ca5b659SJoost Mulders 			 * Set the number of available receive descriptors
28442ca5b659SJoost Mulders 			 * Non-zero values written to this register are added
28452ca5b659SJoost Mulders 			 * to the register's contents. Careful: Writing zero
28462ca5b659SJoost Mulders 			 * clears the register and thus causes a (long) pause
28472ca5b659SJoost Mulders 			 * request.
28482ca5b659SJoost Mulders 			 */
28492ca5b659SJoost Mulders 			VR_PUT8(vrp->acc_reg, VR_FCR0_RXBUFCOUNT,
28502ca5b659SJoost Mulders 			    MIN(vrp->rx.ndesc, 0xFF) -
28512ca5b659SJoost Mulders 			    VR_GET8(vrp->acc_reg,
28522ca5b659SJoost Mulders 			    VR_FCR0_RXBUFCOUNT));
28532ca5b659SJoost Mulders 
28542ca5b659SJoost Mulders 			/*
28552ca5b659SJoost Mulders 			 * Request pause when we have 4 descs left.
28562ca5b659SJoost Mulders 			 */
28572ca5b659SJoost Mulders 			VR_SETBITS8(vrp->acc_reg, VR_FCR1,
28582ca5b659SJoost Mulders 			    VR_FCR1_PAUSEONBITS, VR_FCR1_PAUSEON_04);
28592ca5b659SJoost Mulders 
28602ca5b659SJoost Mulders 			/*
28612ca5b659SJoost Mulders 			 * Cancel the pause when there are 24 descriptors again.
28622ca5b659SJoost Mulders 			 */
28632ca5b659SJoost Mulders 			VR_SETBITS8(vrp->acc_reg, VR_FCR1,
28642ca5b659SJoost Mulders 			    VR_FCR1_PAUSEOFFBITS, VR_FCR1_PAUSEOFF_24);
28652ca5b659SJoost Mulders 
28662ca5b659SJoost Mulders 			/*
28672ca5b659SJoost Mulders 			 * Request a pause of FFFF bit-times. This long pause
28682ca5b659SJoost Mulders 			 * is cancelled when the high watermark is reached.
28692ca5b659SJoost Mulders 			 */
28702ca5b659SJoost Mulders 			VR_PUT16(vrp->acc_reg, VR_FCR2_PAUSE, 0xFFFF);
28712ca5b659SJoost Mulders 
28722ca5b659SJoost Mulders 			/*
28732ca5b659SJoost Mulders 			 * Enable flow control on the MAC.
28742ca5b659SJoost Mulders 			 */
28752ca5b659SJoost Mulders 			VR_SETBIT8(vrp->acc_reg, VR_MISC0, VR_MISC0_FDXTFEN);
28762ca5b659SJoost Mulders 			VR_SETBIT8(vrp->acc_reg, VR_FCR1, VR_FCR1_FD_RX_EN |
28772ca5b659SJoost Mulders 			    VR_FCR1_FD_TX_EN | VR_FCR1_XONXOFF_EN);
28782ca5b659SJoost Mulders 		}
28792ca5b659SJoost Mulders 	} else {
28802ca5b659SJoost Mulders 		/*
28812ca5b659SJoost Mulders 		 * Turn flow control OFF.
28822ca5b659SJoost Mulders 		 */
28832ca5b659SJoost Mulders 		VR_CLRBIT8(vrp->acc_reg,
28842ca5b659SJoost Mulders 		    VR_MISC0, VR_MISC0_FDXRFEN | VR_MISC0_FDXTFEN);
28852ca5b659SJoost Mulders 		if ((vrp->chip.info.features & VR_FEATURE_TX_PAUSE_CAP) != 0) {
28862ca5b659SJoost Mulders 			VR_CLRBIT8(vrp->acc_reg, VR_FCR1,
28872ca5b659SJoost Mulders 			    VR_FCR1_FD_RX_EN | VR_FCR1_FD_TX_EN |
28882ca5b659SJoost Mulders 			    VR_FCR1_XONXOFF_EN);
28892ca5b659SJoost Mulders 		}
28902ca5b659SJoost Mulders 	}
28912ca5b659SJoost Mulders 
28922ca5b659SJoost Mulders 	/*
28932ca5b659SJoost Mulders 	 * Set link state.
28942ca5b659SJoost Mulders 	 */
28952ca5b659SJoost Mulders 	if ((vrp->chip.mii.status & MII_STATUS_LINKUP) != 0)
28962ca5b659SJoost Mulders 		vrp->chip.link.state = VR_LINK_STATE_UP;
28972ca5b659SJoost Mulders 	else
28982ca5b659SJoost Mulders 		vrp->chip.link.state = VR_LINK_STATE_DOWN;
28992ca5b659SJoost Mulders }
29002ca5b659SJoost Mulders 
29012ca5b659SJoost Mulders /*
29022ca5b659SJoost Mulders  * The PHY is automatically polled by the MAC once per 1024 MD clock cycles
29032ca5b659SJoost Mulders  * MD is clocked once per 960ns so polling happens about every 1M ns, some
29042ca5b659SJoost Mulders  * 1000 times per second
29052ca5b659SJoost Mulders  * This polling process is required for the functionality of the link change
29062ca5b659SJoost Mulders  * interrupt. Polling process must be disabled in order to access PHY registers
29072ca5b659SJoost Mulders  * using MDIO
29082ca5b659SJoost Mulders  *
29092ca5b659SJoost Mulders  * Turn off PHY polling so that the PHY registers can be accessed.
29102ca5b659SJoost Mulders  */
29112ca5b659SJoost Mulders static void
vr_phy_autopoll_disable(vr_t * vrp)29122ca5b659SJoost Mulders vr_phy_autopoll_disable(vr_t *vrp)
29132ca5b659SJoost Mulders {
29142ca5b659SJoost Mulders 	uint32_t	time;
29152ca5b659SJoost Mulders 	uint8_t		miicmd, miiaddr;
29162ca5b659SJoost Mulders 
29172ca5b659SJoost Mulders 	/*
29182ca5b659SJoost Mulders 	 * Special procedure to stop the autopolling.
29192ca5b659SJoost Mulders 	 */
29202ca5b659SJoost Mulders 	if ((vrp->chip.info.bugs & VR_BUG_MIIPOLLSTOP) != 0) {
29212ca5b659SJoost Mulders 		/*
29222ca5b659SJoost Mulders 		 * If polling is enabled.
29232ca5b659SJoost Mulders 		 */
29242ca5b659SJoost Mulders 		miicmd = VR_GET8(vrp->acc_reg, VR_MIICMD);
29252ca5b659SJoost Mulders 		if ((miicmd & VR_MIICMD_MD_AUTO) != 0) {
29262ca5b659SJoost Mulders 			/*
29272ca5b659SJoost Mulders 			 * Wait for the end of a cycle (mdone set).
29282ca5b659SJoost Mulders 			 */
29292ca5b659SJoost Mulders 			time = 0;
29302ca5b659SJoost Mulders 			do {
29312ca5b659SJoost Mulders 				drv_usecwait(10);
29322ca5b659SJoost Mulders 				if (time >= VR_MMI_WAITMAX) {
29332ca5b659SJoost Mulders 					vr_log(vrp, CE_WARN,
29342ca5b659SJoost Mulders 					    "Timeout in "
29352ca5b659SJoost Mulders 					    "disable MII polling");
29362ca5b659SJoost Mulders 					break;
29372ca5b659SJoost Mulders 				}
29382ca5b659SJoost Mulders 				time += VR_MMI_WAITINCR;
29392ca5b659SJoost Mulders 				miiaddr = VR_GET8(vrp->acc_reg, VR_MIIADDR);
29402ca5b659SJoost Mulders 			} while ((miiaddr & VR_MIIADDR_MDONE) == 0);
29412ca5b659SJoost Mulders 		}
29422ca5b659SJoost Mulders 		/*
29432ca5b659SJoost Mulders 		 * Once paused, we can disable autopolling.
29442ca5b659SJoost Mulders 		 */
29452ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
29462ca5b659SJoost Mulders 	} else {
29472ca5b659SJoost Mulders 		/*
29482ca5b659SJoost Mulders 		 * Turn off MII polling.
29492ca5b659SJoost Mulders 		 */
29502ca5b659SJoost Mulders 		VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
29512ca5b659SJoost Mulders 
29522ca5b659SJoost Mulders 		/*
29532ca5b659SJoost Mulders 		 * Wait for MIDLE in MII address register.
29542ca5b659SJoost Mulders 		 */
29552ca5b659SJoost Mulders 		time = 0;
29562ca5b659SJoost Mulders 		do {
29572ca5b659SJoost Mulders 			drv_usecwait(VR_MMI_WAITINCR);
29582ca5b659SJoost Mulders 			if (time >= VR_MMI_WAITMAX) {
29592ca5b659SJoost Mulders 				vr_log(vrp, CE_WARN,
29602ca5b659SJoost Mulders 				    "Timeout in disable MII polling");
29612ca5b659SJoost Mulders 				break;
29622ca5b659SJoost Mulders 			}
29632ca5b659SJoost Mulders 			time += VR_MMI_WAITINCR;
29642ca5b659SJoost Mulders 			miiaddr = VR_GET8(vrp->acc_reg, VR_MIIADDR);
29652ca5b659SJoost Mulders 		} while ((miiaddr & VR_MIIADDR_MIDLE) == 0);
29662ca5b659SJoost Mulders 	}
29672ca5b659SJoost Mulders }
29682ca5b659SJoost Mulders 
29692ca5b659SJoost Mulders /*
29702ca5b659SJoost Mulders  * Turn on PHY polling. PHY's registers cannot be accessed.
29712ca5b659SJoost Mulders  */
29722ca5b659SJoost Mulders static void
vr_phy_autopoll_enable(vr_t * vrp)29732ca5b659SJoost Mulders vr_phy_autopoll_enable(vr_t *vrp)
29742ca5b659SJoost Mulders {
29752ca5b659SJoost Mulders 	uint32_t	time;
29762ca5b659SJoost Mulders 
29772ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_MIICMD, 0);
29782ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_MIIADDR, MII_STATUS|VR_MIIADDR_MAUTO);
29792ca5b659SJoost Mulders 	VR_PUT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_AUTO);
29802ca5b659SJoost Mulders 
29812ca5b659SJoost Mulders 	/*
29822ca5b659SJoost Mulders 	 * Wait for the polling process to finish.
29832ca5b659SJoost Mulders 	 */
29842ca5b659SJoost Mulders 	time = 0;
29852ca5b659SJoost Mulders 	do {
29862ca5b659SJoost Mulders 		drv_usecwait(VR_MMI_WAITINCR);
29872ca5b659SJoost Mulders 		if (time >= VR_MMI_WAITMAX) {
29882ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "Timeout in enable MII polling");
29892ca5b659SJoost Mulders 			break;
29902ca5b659SJoost Mulders 		}
29912ca5b659SJoost Mulders 		time += VR_MMI_WAITINCR;
29922ca5b659SJoost Mulders 	} while ((VR_GET8(vrp->acc_reg, VR_MIIADDR) & VR_MIIADDR_MDONE) == 0);
29932ca5b659SJoost Mulders 
29942ca5b659SJoost Mulders 	/*
29952ca5b659SJoost Mulders 	 * Initiate a polling.
29962ca5b659SJoost Mulders 	 */
29972ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_MAUTO);
29982ca5b659SJoost Mulders }
29992ca5b659SJoost Mulders 
30002ca5b659SJoost Mulders /*
30012ca5b659SJoost Mulders  * Read a register from the PHY using MDIO.
30022ca5b659SJoost Mulders  */
30032ca5b659SJoost Mulders static void
vr_phy_read(vr_t * vrp,int offset,uint16_t * value)30042ca5b659SJoost Mulders vr_phy_read(vr_t *vrp, int offset, uint16_t *value)
30052ca5b659SJoost Mulders {
30062ca5b659SJoost Mulders 	uint32_t	time;
30072ca5b659SJoost Mulders 
30082ca5b659SJoost Mulders 	vr_phy_autopoll_disable(vrp);
30092ca5b659SJoost Mulders 
30102ca5b659SJoost Mulders 	/*
30112ca5b659SJoost Mulders 	 * Write the register number to the lower 5 bits of the MII address
30122ca5b659SJoost Mulders 	 * register.
30132ca5b659SJoost Mulders 	 */
30142ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_BITS, offset);
30152ca5b659SJoost Mulders 
30162ca5b659SJoost Mulders 	/*
30172ca5b659SJoost Mulders 	 * Write a READ command to the MII control register
30182ca5b659SJoost Mulders 	 * This bit will be cleared when the read is finished.
30192ca5b659SJoost Mulders 	 */
30202ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_READ);
30212ca5b659SJoost Mulders 
30222ca5b659SJoost Mulders 	/*
30232ca5b659SJoost Mulders 	 * Wait until the read is done.
30242ca5b659SJoost Mulders 	 */
30252ca5b659SJoost Mulders 	time = 0;
30262ca5b659SJoost Mulders 	do {
30272ca5b659SJoost Mulders 		drv_usecwait(VR_MMI_WAITINCR);
30282ca5b659SJoost Mulders 		if (time >= VR_MMI_WAITMAX) {
30292ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "Timeout in MII read command");
30302ca5b659SJoost Mulders 			break;
30312ca5b659SJoost Mulders 		}
30322ca5b659SJoost Mulders 		time += VR_MMI_WAITINCR;
30332ca5b659SJoost Mulders 	} while ((VR_GET8(vrp->acc_reg, VR_MIICMD) & VR_MIICMD_MD_READ) != 0);
30342ca5b659SJoost Mulders 
30352ca5b659SJoost Mulders 	*value = VR_GET16(vrp->acc_reg, VR_MIIDATA);
30362ca5b659SJoost Mulders 	vr_phy_autopoll_enable(vrp);
30372ca5b659SJoost Mulders }
30382ca5b659SJoost Mulders 
30392ca5b659SJoost Mulders /*
30402ca5b659SJoost Mulders  * Write to a PHY's register.
30412ca5b659SJoost Mulders  */
30422ca5b659SJoost Mulders static void
vr_phy_write(vr_t * vrp,int offset,uint16_t value)30432ca5b659SJoost Mulders vr_phy_write(vr_t *vrp, int offset, uint16_t value)
30442ca5b659SJoost Mulders {
30452ca5b659SJoost Mulders 	uint32_t	time;
30462ca5b659SJoost Mulders 
30472ca5b659SJoost Mulders 	vr_phy_autopoll_disable(vrp);
30482ca5b659SJoost Mulders 
30492ca5b659SJoost Mulders 	/*
30502ca5b659SJoost Mulders 	 * Write the register number to the MII address register.
30512ca5b659SJoost Mulders 	 */
30522ca5b659SJoost Mulders 	VR_SETBITS8(vrp->acc_reg, VR_MIIADDR, VR_MIIADDR_BITS, offset);
30532ca5b659SJoost Mulders 
30542ca5b659SJoost Mulders 	/*
30552ca5b659SJoost Mulders 	 * Write the value to the data register.
30562ca5b659SJoost Mulders 	 */
30572ca5b659SJoost Mulders 	VR_PUT16(vrp->acc_reg, VR_MIIDATA, value);
30582ca5b659SJoost Mulders 
30592ca5b659SJoost Mulders 	/*
30602ca5b659SJoost Mulders 	 * Issue the WRITE command to the command register.
30612ca5b659SJoost Mulders 	 * This bit will be cleared when the write is finished.
30622ca5b659SJoost Mulders 	 */
30632ca5b659SJoost Mulders 	VR_SETBIT8(vrp->acc_reg, VR_MIICMD, VR_MIICMD_MD_WRITE);
30642ca5b659SJoost Mulders 
30652ca5b659SJoost Mulders 	time = 0;
30662ca5b659SJoost Mulders 	do {
30672ca5b659SJoost Mulders 		drv_usecwait(VR_MMI_WAITINCR);
30682ca5b659SJoost Mulders 		if (time >= VR_MMI_WAITMAX) {
30692ca5b659SJoost Mulders 			vr_log(vrp, CE_NOTE, "Timeout in MII write command");
30702ca5b659SJoost Mulders 			break;
30712ca5b659SJoost Mulders 		}
30722ca5b659SJoost Mulders 		time += VR_MMI_WAITINCR;
30732ca5b659SJoost Mulders 	} while ((VR_GET8(vrp->acc_reg, VR_MIICMD) & VR_MIICMD_MD_WRITE) != 0);
30742ca5b659SJoost Mulders 	vr_phy_autopoll_enable(vrp);
30752ca5b659SJoost Mulders }
30762ca5b659SJoost Mulders 
30772ca5b659SJoost Mulders /*
30782ca5b659SJoost Mulders  * Initialize and install some private kstats.
30792ca5b659SJoost Mulders  */
30802ca5b659SJoost Mulders typedef struct {
30812ca5b659SJoost Mulders 	char		*name;
30822ca5b659SJoost Mulders 	uchar_t		type;
30832ca5b659SJoost Mulders } vr_kstat_t;
30842ca5b659SJoost Mulders 
30852ca5b659SJoost Mulders static const vr_kstat_t vr_driver_stats [] = {
30862ca5b659SJoost Mulders 	{"allocbfail",		KSTAT_DATA_INT32},
30872ca5b659SJoost Mulders 	{"intr_claimed",	KSTAT_DATA_INT64},
30882ca5b659SJoost Mulders 	{"intr_unclaimed",	KSTAT_DATA_INT64},
30892ca5b659SJoost Mulders 	{"linkchanges",		KSTAT_DATA_INT64},
30902ca5b659SJoost Mulders 	{"txnfree",		KSTAT_DATA_INT32},
30912ca5b659SJoost Mulders 	{"txstalls",		KSTAT_DATA_INT32},
30922ca5b659SJoost Mulders 	{"resets",		KSTAT_DATA_INT32},
30932ca5b659SJoost Mulders 	{"txreclaims",		KSTAT_DATA_INT64},
30942ca5b659SJoost Mulders 	{"txreclaim0",		KSTAT_DATA_INT64},
30952ca5b659SJoost Mulders 	{"cyclics",		KSTAT_DATA_INT64},
30962ca5b659SJoost Mulders 	{"txchecks",		KSTAT_DATA_INT64},
30972ca5b659SJoost Mulders };
30982ca5b659SJoost Mulders 
30992ca5b659SJoost Mulders static void
vr_kstats_init(vr_t * vrp)31002ca5b659SJoost Mulders vr_kstats_init(vr_t *vrp)
31012ca5b659SJoost Mulders {
31022ca5b659SJoost Mulders 	kstat_t			*ksp;
31032ca5b659SJoost Mulders 	struct	kstat_named	*knp;
31042ca5b659SJoost Mulders 	int			i;
31052ca5b659SJoost Mulders 	int			nstats;
31062ca5b659SJoost Mulders 
31072ca5b659SJoost Mulders 	nstats = sizeof (vr_driver_stats) / sizeof (vr_kstat_t);
31082ca5b659SJoost Mulders 
31092ca5b659SJoost Mulders 	ksp = kstat_create(MODULENAME, ddi_get_instance(vrp->devinfo),
31102ca5b659SJoost Mulders 	    "driver", "net", KSTAT_TYPE_NAMED, nstats, 0);
31112ca5b659SJoost Mulders 
31122ca5b659SJoost Mulders 	if (ksp == NULL)
31132ca5b659SJoost Mulders 		vr_log(vrp, CE_WARN, "kstat_create failed");
31142ca5b659SJoost Mulders 
31152ca5b659SJoost Mulders 	ksp->ks_update = vr_update_kstats;
31162ca5b659SJoost Mulders 	ksp->ks_private = (void*) vrp;
31172ca5b659SJoost Mulders 	knp = ksp->ks_data;
31182ca5b659SJoost Mulders 
31192ca5b659SJoost Mulders 	for (i = 0; i < nstats; i++, knp++) {
31202ca5b659SJoost Mulders 		kstat_named_init(knp, vr_driver_stats[i].name,
31212ca5b659SJoost Mulders 		    vr_driver_stats[i].type);
31222ca5b659SJoost Mulders 	}
31232ca5b659SJoost Mulders 	kstat_install(ksp);
31242ca5b659SJoost Mulders 	vrp->ksp = ksp;
31252ca5b659SJoost Mulders }
31262ca5b659SJoost Mulders 
31272ca5b659SJoost Mulders static int
vr_update_kstats(kstat_t * ksp,int access)31282ca5b659SJoost Mulders vr_update_kstats(kstat_t *ksp, int access)
31292ca5b659SJoost Mulders {
31302ca5b659SJoost Mulders 	vr_t			*vrp;
31312ca5b659SJoost Mulders 	struct kstat_named	*knp;
31322ca5b659SJoost Mulders 
31332ca5b659SJoost Mulders 	vrp = (vr_t *)ksp->ks_private;
31342ca5b659SJoost Mulders 	knp = ksp->ks_data;
31352ca5b659SJoost Mulders 
31362ca5b659SJoost Mulders 	if (access != KSTAT_READ)
31372ca5b659SJoost Mulders 		return (EACCES);
31382ca5b659SJoost Mulders 
31392ca5b659SJoost Mulders 	(knp++)->value.ui32 = vrp->stats.allocbfail;
31402ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.intr_claimed;
31412ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.intr_unclaimed;
31422ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.linkchanges;
31432ca5b659SJoost Mulders 	(knp++)->value.ui32 = vrp->tx.nfree;
31442ca5b659SJoost Mulders 	(knp++)->value.ui32 = vrp->stats.txstalls;
31452ca5b659SJoost Mulders 	(knp++)->value.ui32 = vrp->stats.resets;
31462ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.txreclaims;
31472ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.txreclaim0;
31482ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.cyclics;
31492ca5b659SJoost Mulders 	(knp++)->value.ui64 = vrp->stats.txchecks;
31502ca5b659SJoost Mulders 	return (0);
31512ca5b659SJoost Mulders }
31522ca5b659SJoost Mulders 
31532ca5b659SJoost Mulders /*
31542ca5b659SJoost Mulders  * Remove 'private' kstats.
31552ca5b659SJoost Mulders  */
31562ca5b659SJoost Mulders static void
vr_remove_kstats(vr_t * vrp)31572ca5b659SJoost Mulders vr_remove_kstats(vr_t *vrp)
31582ca5b659SJoost Mulders {
31592ca5b659SJoost Mulders 	if (vrp->ksp != NULL)
31602ca5b659SJoost Mulders 		kstat_delete(vrp->ksp);
31612ca5b659SJoost Mulders }
31622ca5b659SJoost Mulders 
31632ca5b659SJoost Mulders /*
31642ca5b659SJoost Mulders  * Get a property of the device/driver
31652ca5b659SJoost Mulders  * Remarks:
31662ca5b659SJoost Mulders  * - pr_val is always an integer of size pr_valsize
31672ca5b659SJoost Mulders  * - ENABLED (EN) is what is configured via dladm
31682ca5b659SJoost Mulders  * - ADVERTISED (ADV) is ENABLED minus constraints, like PHY/MAC capabilities
31692ca5b659SJoost Mulders  * - DEFAULT are driver- and hardware defaults (DEFAULT is implemented as a
31702ca5b659SJoost Mulders  *   flag in pr_flags instead of MAC_PROP_DEFAULT_)
31712ca5b659SJoost Mulders  * - perm is the permission printed on ndd -get /.. \?
31722ca5b659SJoost Mulders  */
31732ca5b659SJoost Mulders int
vr_mac_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)31742ca5b659SJoost Mulders vr_mac_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
31750dc2366fSVenugopal Iyer     uint_t pr_valsize, void *pr_val)
31762ca5b659SJoost Mulders {
31772ca5b659SJoost Mulders 	vr_t		*vrp;
31782ca5b659SJoost Mulders 	uint32_t	err;
31792ca5b659SJoost Mulders 	uint64_t	val;
31802ca5b659SJoost Mulders 
31812ca5b659SJoost Mulders 	/* Since we have no private properties */
31822ca5b659SJoost Mulders 	_NOTE(ARGUNUSED(pr_name))
31832ca5b659SJoost Mulders 
31842ca5b659SJoost Mulders 	err = 0;
31852ca5b659SJoost Mulders 	vrp = (vr_t *)arg;
31860dc2366fSVenugopal Iyer 	switch (pr_num) {
31870dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_1000FDX_CAP:
31880dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_1000HDX_CAP:
31890dc2366fSVenugopal Iyer 		case MAC_PROP_EN_1000FDX_CAP:
31900dc2366fSVenugopal Iyer 		case MAC_PROP_EN_1000HDX_CAP:
31910dc2366fSVenugopal Iyer 			val = 0;
31920dc2366fSVenugopal Iyer 			break;
31932ca5b659SJoost Mulders 
31940dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100FDX_CAP:
31950dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.anadv &
31960dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_TX_FD) != 0;
31970dc2366fSVenugopal Iyer 			break;
31982ca5b659SJoost Mulders 
31990dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100HDX_CAP:
32000dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.anadv &
32010dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_TX) != 0;
32020dc2366fSVenugopal Iyer 			break;
32032ca5b659SJoost Mulders 
32040dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100T4_CAP:
32050dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.anadv &
32060dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_T4) != 0;
32070dc2366fSVenugopal Iyer 			break;
32082ca5b659SJoost Mulders 
32090dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_10FDX_CAP:
32100dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.anadv &
32110dc2366fSVenugopal Iyer 			    MII_ABILITY_10BASE_T_FD) != 0;
32120dc2366fSVenugopal Iyer 			break;
32132ca5b659SJoost Mulders 
32140dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_10HDX_CAP:
32150dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.anadv &
32160dc2366fSVenugopal Iyer 			    MII_ABILITY_10BASE_T) != 0;
32170dc2366fSVenugopal Iyer 			break;
32182ca5b659SJoost Mulders 
32190dc2366fSVenugopal Iyer 		case MAC_PROP_AUTONEG:
32200dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.control &
32210dc2366fSVenugopal Iyer 			    MII_CONTROL_ANE) != 0;
32220dc2366fSVenugopal Iyer 			break;
32232ca5b659SJoost Mulders 
32240dc2366fSVenugopal Iyer 		case MAC_PROP_DUPLEX:
32250dc2366fSVenugopal Iyer 			val = vrp->chip.link.duplex;
32260dc2366fSVenugopal Iyer 			break;
32272ca5b659SJoost Mulders 
32280dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100FDX_CAP:
32290dc2366fSVenugopal Iyer 			val = (vrp->param.anadv_en &
32300dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_TX_FD) != 0;
32310dc2366fSVenugopal Iyer 			break;
32322ca5b659SJoost Mulders 
32330dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100HDX_CAP:
32340dc2366fSVenugopal Iyer 			val = (vrp->param.anadv_en &
32350dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_TX) != 0;
32360dc2366fSVenugopal Iyer 			break;
32372ca5b659SJoost Mulders 
32380dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100T4_CAP:
32390dc2366fSVenugopal Iyer 			val = (vrp->param.anadv_en &
32400dc2366fSVenugopal Iyer 			    MII_ABILITY_100BASE_T4) != 0;
32410dc2366fSVenugopal Iyer 			break;
32422ca5b659SJoost Mulders 
32430dc2366fSVenugopal Iyer 		case MAC_PROP_EN_10FDX_CAP:
32440dc2366fSVenugopal Iyer 			val = (vrp->param.anadv_en &
32450dc2366fSVenugopal Iyer 			    MII_ABILITY_10BASE_T_FD) != 0;
32460dc2366fSVenugopal Iyer 			break;
32472ca5b659SJoost Mulders 
32480dc2366fSVenugopal Iyer 		case MAC_PROP_EN_10HDX_CAP:
32490dc2366fSVenugopal Iyer 			val = (vrp->param.anadv_en &
32500dc2366fSVenugopal Iyer 			    MII_ABILITY_10BASE_T) != 0;
32510dc2366fSVenugopal Iyer 			break;
32520dc2366fSVenugopal Iyer 
32530dc2366fSVenugopal Iyer 		case MAC_PROP_EN_AUTONEG:
32540dc2366fSVenugopal Iyer 			val = vrp->param.an_en == VR_LINK_AUTONEG_ON;
32550dc2366fSVenugopal Iyer 			break;
32562ca5b659SJoost Mulders 
32570dc2366fSVenugopal Iyer 		case MAC_PROP_FLOWCTRL:
32580dc2366fSVenugopal Iyer 			val = vrp->chip.link.flowctrl;
32590dc2366fSVenugopal Iyer 			break;
32600dc2366fSVenugopal Iyer 
32610dc2366fSVenugopal Iyer 		case MAC_PROP_MTU:
32620dc2366fSVenugopal Iyer 			val = vrp->param.mtu;
32630dc2366fSVenugopal Iyer 			break;
32640dc2366fSVenugopal Iyer 
32650dc2366fSVenugopal Iyer 		case MAC_PROP_SPEED:
32660dc2366fSVenugopal Iyer 			if (vrp->chip.link.speed ==
32670dc2366fSVenugopal Iyer 			    VR_LINK_SPEED_100MBS)
32680dc2366fSVenugopal Iyer 				val = 100 * 1000 * 1000;
32690dc2366fSVenugopal Iyer 			else if (vrp->chip.link.speed ==
32700dc2366fSVenugopal Iyer 			    VR_LINK_SPEED_10MBS)
32710dc2366fSVenugopal Iyer 				val = 10 * 1000 * 1000;
32720dc2366fSVenugopal Iyer 			else
32732ca5b659SJoost Mulders 				val = 0;
32740dc2366fSVenugopal Iyer 			break;
32752ca5b659SJoost Mulders 
32760dc2366fSVenugopal Iyer 		case MAC_PROP_STATUS:
32770dc2366fSVenugopal Iyer 			val = vrp->chip.link.state;
32780dc2366fSVenugopal Iyer 			break;
32792ca5b659SJoost Mulders 
32800dc2366fSVenugopal Iyer 		default:
32810dc2366fSVenugopal Iyer 			err = ENOTSUP;
32820dc2366fSVenugopal Iyer 			break;
32830dc2366fSVenugopal Iyer 	}
32842ca5b659SJoost Mulders 
32850dc2366fSVenugopal Iyer 	if (err == 0 && pr_num != MAC_PROP_PRIVATE) {
32860dc2366fSVenugopal Iyer 		if (pr_valsize == sizeof (uint64_t))
32870dc2366fSVenugopal Iyer 			*(uint64_t *)pr_val = val;
32880dc2366fSVenugopal Iyer 		else if (pr_valsize == sizeof (uint32_t))
32890dc2366fSVenugopal Iyer 			*(uint32_t *)pr_val = val;
32900dc2366fSVenugopal Iyer 		else if (pr_valsize == sizeof (uint16_t))
32910dc2366fSVenugopal Iyer 			*(uint16_t *)pr_val = val;
32920dc2366fSVenugopal Iyer 		else if (pr_valsize == sizeof (uint8_t))
32930dc2366fSVenugopal Iyer 			*(uint8_t *)pr_val = val;
32940dc2366fSVenugopal Iyer 		else
32950dc2366fSVenugopal Iyer 			err = EINVAL;
32960dc2366fSVenugopal Iyer 	}
32970dc2366fSVenugopal Iyer 	return (err);
32980dc2366fSVenugopal Iyer }
32992ca5b659SJoost Mulders 
33000dc2366fSVenugopal Iyer void
vr_mac_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)33010dc2366fSVenugopal Iyer vr_mac_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
33020dc2366fSVenugopal Iyer     mac_prop_info_handle_t prh)
33030dc2366fSVenugopal Iyer {
33040dc2366fSVenugopal Iyer 	vr_t		*vrp = (vr_t *)arg;
33050dc2366fSVenugopal Iyer 	uint8_t		val, perm;
33062ca5b659SJoost Mulders 
33070dc2366fSVenugopal Iyer 	/* Since we have no private properties */
33080dc2366fSVenugopal Iyer 	_NOTE(ARGUNUSED(pr_name))
33092ca5b659SJoost Mulders 
33100dc2366fSVenugopal Iyer 	switch (pr_num) {
33110dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_1000FDX_CAP:
33120dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_1000HDX_CAP:
33130dc2366fSVenugopal Iyer 		case MAC_PROP_EN_1000FDX_CAP:
33140dc2366fSVenugopal Iyer 		case MAC_PROP_EN_1000HDX_CAP:
33150dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100FDX_CAP:
33160dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100HDX_CAP:
33170dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_100T4_CAP:
33180dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_10FDX_CAP:
33190dc2366fSVenugopal Iyer 		case MAC_PROP_ADV_10HDX_CAP:
33200dc2366fSVenugopal Iyer 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
33210dc2366fSVenugopal Iyer 			return;
33220dc2366fSVenugopal Iyer 
33230dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100FDX_CAP:
33240dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33250dc2366fSVenugopal Iyer 			    MII_STATUS_100_BASEX_FD) != 0;
33260dc2366fSVenugopal Iyer 			break;
33272ca5b659SJoost Mulders 
33280dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100HDX_CAP:
33290dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33300dc2366fSVenugopal Iyer 			    MII_STATUS_100_BASEX) != 0;
33310dc2366fSVenugopal Iyer 			break;
33322ca5b659SJoost Mulders 
33330dc2366fSVenugopal Iyer 		case MAC_PROP_EN_100T4_CAP:
33340dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33350dc2366fSVenugopal Iyer 			    MII_STATUS_100_BASE_T4) != 0;
33360dc2366fSVenugopal Iyer 			break;
33372ca5b659SJoost Mulders 
33380dc2366fSVenugopal Iyer 		case MAC_PROP_EN_10FDX_CAP:
33390dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33400dc2366fSVenugopal Iyer 			    MII_STATUS_10_FD) != 0;
33410dc2366fSVenugopal Iyer 			break;
33422ca5b659SJoost Mulders 
33430dc2366fSVenugopal Iyer 		case MAC_PROP_EN_10HDX_CAP:
33440dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33450dc2366fSVenugopal Iyer 			    MII_STATUS_10) != 0;
33460dc2366fSVenugopal Iyer 			break;
33472ca5b659SJoost Mulders 
33480dc2366fSVenugopal Iyer 		case MAC_PROP_AUTONEG:
33490dc2366fSVenugopal Iyer 		case MAC_PROP_EN_AUTONEG:
33500dc2366fSVenugopal Iyer 			val = (vrp->chip.mii.status &
33510dc2366fSVenugopal Iyer 			    MII_STATUS_CANAUTONEG) != 0;
33520dc2366fSVenugopal Iyer 			break;
33532ca5b659SJoost Mulders 
33540dc2366fSVenugopal Iyer 		case MAC_PROP_FLOWCTRL:
33550dc2366fSVenugopal Iyer 			mac_prop_info_set_default_link_flowctrl(prh,
33560dc2366fSVenugopal Iyer 			    LINK_FLOWCTRL_BI);
33570dc2366fSVenugopal Iyer 			return;
33582ca5b659SJoost Mulders 
33590dc2366fSVenugopal Iyer 		case MAC_PROP_MTU:
33600dc2366fSVenugopal Iyer 			mac_prop_info_set_range_uint32(prh,
33610dc2366fSVenugopal Iyer 			    ETHERMTU, ETHERMTU);
33620dc2366fSVenugopal Iyer 			return;
33632ca5b659SJoost Mulders 
33640dc2366fSVenugopal Iyer 		case MAC_PROP_DUPLEX:
33650dc2366fSVenugopal Iyer 			/*
33660dc2366fSVenugopal Iyer 			 * Writability depends on autoneg.
33670dc2366fSVenugopal Iyer 			 */
33680dc2366fSVenugopal Iyer 			perm = ((vrp->chip.mii.control &
33690dc2366fSVenugopal Iyer 			    MII_CONTROL_ANE) == 0) ? MAC_PROP_PERM_RW :
33700dc2366fSVenugopal Iyer 			    MAC_PROP_PERM_READ;
33710dc2366fSVenugopal Iyer 			mac_prop_info_set_perm(prh, perm);
33720dc2366fSVenugopal Iyer 
33730dc2366fSVenugopal Iyer 			if (perm == MAC_PROP_PERM_RW) {
33740dc2366fSVenugopal Iyer 				mac_prop_info_set_default_uint8(prh,
33750dc2366fSVenugopal Iyer 				    VR_LINK_DUPLEX_FULL);
33760dc2366fSVenugopal Iyer 			}
33770dc2366fSVenugopal Iyer 			return;
33782ca5b659SJoost Mulders 
33790dc2366fSVenugopal Iyer 		case MAC_PROP_SPEED:
33800dc2366fSVenugopal Iyer 			perm = ((vrp->chip.mii.control &
33810dc2366fSVenugopal Iyer 			    MII_CONTROL_ANE) == 0) ?
33820dc2366fSVenugopal Iyer 			    MAC_PROP_PERM_RW : MAC_PROP_PERM_READ;
33830dc2366fSVenugopal Iyer 			mac_prop_info_set_perm(prh, perm);
33842ca5b659SJoost Mulders 
33850dc2366fSVenugopal Iyer 			if (perm == MAC_PROP_PERM_RW) {
33860dc2366fSVenugopal Iyer 				mac_prop_info_set_default_uint64(prh,
33870dc2366fSVenugopal Iyer 				    100 * 1000 * 1000);
33880dc2366fSVenugopal Iyer 			}
33890dc2366fSVenugopal Iyer 			return;
33902ca5b659SJoost Mulders 
33910dc2366fSVenugopal Iyer 		case MAC_PROP_STATUS:
33920dc2366fSVenugopal Iyer 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
33930dc2366fSVenugopal Iyer 			return;
33942ca5b659SJoost Mulders 
33950dc2366fSVenugopal Iyer 		default:
33960dc2366fSVenugopal Iyer 			return;
339715c07adcSJohn Levon 	}
33980dc2366fSVenugopal Iyer 
339915c07adcSJohn Levon 	mac_prop_info_set_default_uint8(prh, val);
34002ca5b659SJoost Mulders }
34012ca5b659SJoost Mulders 
34022ca5b659SJoost Mulders /*
34032ca5b659SJoost Mulders  * Set a property of the device.
34042ca5b659SJoost Mulders  */
34052ca5b659SJoost Mulders int
vr_mac_setprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)34062ca5b659SJoost Mulders vr_mac_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
3407*cfe080a1SToomas Soome     uint_t pr_valsize, const void *pr_val)
34082ca5b659SJoost Mulders {
34092ca5b659SJoost Mulders 	vr_t		*vrp;
34102ca5b659SJoost Mulders 	uint32_t	err;
34112ca5b659SJoost Mulders 	uint64_t	val;
34122ca5b659SJoost Mulders 
34132ca5b659SJoost Mulders 	/* Since we have no private properties */
34142ca5b659SJoost Mulders 	_NOTE(ARGUNUSED(pr_name))
34152ca5b659SJoost Mulders 
34162ca5b659SJoost Mulders 	err = 0;
34172ca5b659SJoost Mulders 	vrp = (vr_t *)arg;
34182ca5b659SJoost Mulders 	mutex_enter(&vrp->oplock);
34192ca5b659SJoost Mulders 
34202ca5b659SJoost Mulders 	/*
34212ca5b659SJoost Mulders 	 * The current set of public property values are passed as integers
34222ca5b659SJoost Mulders 	 * Private properties are passed as strings in pr_val length pr_valsize.
34232ca5b659SJoost Mulders 	 */
34242ca5b659SJoost Mulders 	if (pr_num != MAC_PROP_PRIVATE) {
34252ca5b659SJoost Mulders 		if (pr_valsize == sizeof (uint64_t))
34262ca5b659SJoost Mulders 			val = *(uint64_t *)pr_val;
34272ca5b659SJoost Mulders 		else if (pr_valsize == sizeof (uint32_t))
34282ca5b659SJoost Mulders 			val = *(uint32_t *)pr_val;
34292ca5b659SJoost Mulders 		else if (pr_valsize == sizeof (uint16_t))
34302ca5b659SJoost Mulders 			val = *(uint32_t *)pr_val;
34312ca5b659SJoost Mulders 		else if (pr_valsize == sizeof (uint8_t))
34322ca5b659SJoost Mulders 			val = *(uint8_t *)pr_val;
34332ca5b659SJoost Mulders 		else {
34342ca5b659SJoost Mulders 			mutex_exit(&vrp->oplock);
34352ca5b659SJoost Mulders 			return (EINVAL);
34362ca5b659SJoost Mulders 		}
34372ca5b659SJoost Mulders 	}
34382ca5b659SJoost Mulders 
34392ca5b659SJoost Mulders 	switch (pr_num) {
34402ca5b659SJoost Mulders 		case MAC_PROP_DUPLEX:
34412ca5b659SJoost Mulders 			if ((vrp->chip.mii.control & MII_CONTROL_ANE) == 0) {
34422ca5b659SJoost Mulders 				if (val == LINK_DUPLEX_FULL)
34432ca5b659SJoost Mulders 					vrp->chip.mii.control |=
34442ca5b659SJoost Mulders 					    MII_CONTROL_FDUPLEX;
34452ca5b659SJoost Mulders 				else if (val == LINK_DUPLEX_HALF)
34462ca5b659SJoost Mulders 					vrp->chip.mii.control &=
34472ca5b659SJoost Mulders 					    ~MII_CONTROL_FDUPLEX;
34482ca5b659SJoost Mulders 				else
34492ca5b659SJoost Mulders 					err = EINVAL;
34502ca5b659SJoost Mulders 			} else
34512ca5b659SJoost Mulders 				err = EINVAL;
34522ca5b659SJoost Mulders 			break;
34532ca5b659SJoost Mulders 
34542ca5b659SJoost Mulders 		case MAC_PROP_EN_100FDX_CAP:
34552ca5b659SJoost Mulders 			if (val == 0)
34562ca5b659SJoost Mulders 				vrp->param.anadv_en &=
34572ca5b659SJoost Mulders 				    ~MII_ABILITY_100BASE_TX_FD;
34582ca5b659SJoost Mulders 			else
34592ca5b659SJoost Mulders 				vrp->param.anadv_en |=
34602ca5b659SJoost Mulders 				    MII_ABILITY_100BASE_TX_FD;
34612ca5b659SJoost Mulders 			break;
34622ca5b659SJoost Mulders 
34632ca5b659SJoost Mulders 		case MAC_PROP_EN_100HDX_CAP:
34642ca5b659SJoost Mulders 			if (val == 0)
34652ca5b659SJoost Mulders 				vrp->param.anadv_en &=
34662ca5b659SJoost Mulders 				    ~MII_ABILITY_100BASE_TX;
34672ca5b659SJoost Mulders 			else
34682ca5b659SJoost Mulders 				vrp->param.anadv_en |=
34692ca5b659SJoost Mulders 				    MII_ABILITY_100BASE_TX;
34702ca5b659SJoost Mulders 			break;
34712ca5b659SJoost Mulders 
34722ca5b659SJoost Mulders 		case MAC_PROP_EN_100T4_CAP:
34732ca5b659SJoost Mulders 			if (val == 0)
34742ca5b659SJoost Mulders 				vrp->param.anadv_en &=
34752ca5b659SJoost Mulders 				    ~MII_ABILITY_100BASE_T4;
34762ca5b659SJoost Mulders 			else
34772ca5b659SJoost Mulders 				vrp->param.anadv_en |=
34782ca5b659SJoost Mulders 				    MII_ABILITY_100BASE_T4;
34792ca5b659SJoost Mulders 			break;
34802ca5b659SJoost Mulders 
34812ca5b659SJoost Mulders 		case MAC_PROP_EN_10FDX_CAP:
34822ca5b659SJoost Mulders 			if (val == 0)
34832ca5b659SJoost Mulders 				vrp->param.anadv_en &=
34842ca5b659SJoost Mulders 				    ~MII_ABILITY_10BASE_T_FD;
34852ca5b659SJoost Mulders 			else
34862ca5b659SJoost Mulders 				vrp->param.anadv_en |=
34872ca5b659SJoost Mulders 				    MII_ABILITY_10BASE_T_FD;
34882ca5b659SJoost Mulders 			break;
34892ca5b659SJoost Mulders 
34902ca5b659SJoost Mulders 		case MAC_PROP_EN_10HDX_CAP:
34912ca5b659SJoost Mulders 			if (val == 0)
34922ca5b659SJoost Mulders 				vrp->param.anadv_en &=
34932ca5b659SJoost Mulders 				    ~MII_ABILITY_10BASE_T;
34942ca5b659SJoost Mulders 			else
34952ca5b659SJoost Mulders 				vrp->param.anadv_en |=
34962ca5b659SJoost Mulders 				    MII_ABILITY_10BASE_T;
34972ca5b659SJoost Mulders 			break;
34982ca5b659SJoost Mulders 
34992ca5b659SJoost Mulders 		case MAC_PROP_AUTONEG:
35002ca5b659SJoost Mulders 		case MAC_PROP_EN_AUTONEG:
35012ca5b659SJoost Mulders 			if (val == 0) {
35022ca5b659SJoost Mulders 				vrp->param.an_en = VR_LINK_AUTONEG_OFF;
35032ca5b659SJoost Mulders 				vrp->chip.mii.control &= ~MII_CONTROL_ANE;
35042ca5b659SJoost Mulders 			} else {
35052ca5b659SJoost Mulders 				vrp->param.an_en = VR_LINK_AUTONEG_ON;
35062ca5b659SJoost Mulders 				if ((vrp->chip.mii.status &
35072ca5b659SJoost Mulders 				    MII_STATUS_CANAUTONEG) != 0)
35082ca5b659SJoost Mulders 					vrp->chip.mii.control |=
35092ca5b659SJoost Mulders 					    MII_CONTROL_ANE;
35102ca5b659SJoost Mulders 				else
35112ca5b659SJoost Mulders 					err = EINVAL;
35122ca5b659SJoost Mulders 			}
35132ca5b659SJoost Mulders 			break;
35142ca5b659SJoost Mulders 
35152ca5b659SJoost Mulders 		case MAC_PROP_FLOWCTRL:
35162ca5b659SJoost Mulders 			if (val == LINK_FLOWCTRL_NONE)
3517bdb9230aSGarrett D'Amore 				vrp->param.anadv_en &= ~MII_ABILITY_PAUSE;
35182ca5b659SJoost Mulders 			else if (val == LINK_FLOWCTRL_BI)
3519bdb9230aSGarrett D'Amore 				vrp->param.anadv_en |= MII_ABILITY_PAUSE;
35202ca5b659SJoost Mulders 			else
35212ca5b659SJoost Mulders 				err = EINVAL;
35222ca5b659SJoost Mulders 			break;
35232ca5b659SJoost Mulders 
35242ca5b659SJoost Mulders 		case MAC_PROP_MTU:
35252ca5b659SJoost Mulders 			if (val >= ETHERMIN && val <= ETHERMTU)
35262ca5b659SJoost Mulders 				vrp->param.mtu = (uint32_t)val;
35272ca5b659SJoost Mulders 			else
35282ca5b659SJoost Mulders 				err = EINVAL;
35292ca5b659SJoost Mulders 			break;
35302ca5b659SJoost Mulders 
35312ca5b659SJoost Mulders 		case MAC_PROP_SPEED:
35322ca5b659SJoost Mulders 			if (val == 10 * 1000 * 1000)
35332ca5b659SJoost Mulders 				vrp->chip.link.speed =
35342ca5b659SJoost Mulders 				    VR_LINK_SPEED_10MBS;
35352ca5b659SJoost Mulders 			else if (val == 100 * 1000 * 1000)
35362ca5b659SJoost Mulders 				vrp->chip.link.speed =
35372ca5b659SJoost Mulders 				    VR_LINK_SPEED_100MBS;
35382ca5b659SJoost Mulders 			else
35392ca5b659SJoost Mulders 				err = EINVAL;
35402ca5b659SJoost Mulders 			break;
35412ca5b659SJoost Mulders 
35422ca5b659SJoost Mulders 		default:
35432ca5b659SJoost Mulders 			err = ENOTSUP;
35442ca5b659SJoost Mulders 			break;
35452ca5b659SJoost Mulders 	}
35462ca5b659SJoost Mulders 	if (err == 0 && pr_num != MAC_PROP_PRIVATE) {
35472ca5b659SJoost Mulders 		vrp->chip.mii.anadv = vrp->param.anadv_en &
35482ca5b659SJoost Mulders 		    (vrp->param.an_phymask & vrp->param.an_macmask);
35492ca5b659SJoost Mulders 		vr_link_init(vrp);
35502ca5b659SJoost Mulders 	}
35512ca5b659SJoost Mulders 	mutex_exit(&vrp->oplock);
35522ca5b659SJoost Mulders 	return (err);
35532ca5b659SJoost Mulders }
35542ca5b659SJoost Mulders 
35552ca5b659SJoost Mulders 
35562ca5b659SJoost Mulders /*
35572ca5b659SJoost Mulders  * Logging and debug functions.
35582ca5b659SJoost Mulders  */
35592ca5b659SJoost Mulders static struct {
35602ca5b659SJoost Mulders 	kmutex_t mutex[1];
35612ca5b659SJoost Mulders 	const char *ifname;
35622ca5b659SJoost Mulders 	const char *fmt;
35632ca5b659SJoost Mulders 	int level;
35642ca5b659SJoost Mulders } prtdata;
35652ca5b659SJoost Mulders 
35662ca5b659SJoost Mulders static void
vr_vprt(const char * fmt,va_list args)35672ca5b659SJoost Mulders vr_vprt(const char *fmt, va_list args)
35682ca5b659SJoost Mulders {
35692ca5b659SJoost Mulders 	char buf[512];
35702ca5b659SJoost Mulders 
35712ca5b659SJoost Mulders 	ASSERT(mutex_owned(prtdata.mutex));
35722ca5b659SJoost Mulders 	(void) vsnprintf(buf, sizeof (buf), fmt, args);
35732ca5b659SJoost Mulders 	cmn_err(prtdata.level, prtdata.fmt, prtdata.ifname, buf);
35742ca5b659SJoost Mulders }
35752ca5b659SJoost Mulders 
35762ca5b659SJoost Mulders static void
vr_log(vr_t * vrp,int level,const char * fmt,...)35772ca5b659SJoost Mulders vr_log(vr_t *vrp, int level, const char *fmt, ...)
35782ca5b659SJoost Mulders {
35792ca5b659SJoost Mulders 	va_list args;
35802ca5b659SJoost Mulders 
35812ca5b659SJoost Mulders 	mutex_enter(prtdata.mutex);
35822ca5b659SJoost Mulders 	prtdata.ifname = vrp->ifname;
35832ca5b659SJoost Mulders 	prtdata.fmt = "!%s: %s";
35842ca5b659SJoost Mulders 	prtdata.level = level;
35852ca5b659SJoost Mulders 
35862ca5b659SJoost Mulders 	va_start(args, fmt);
35872ca5b659SJoost Mulders 	vr_vprt(fmt, args);
35882ca5b659SJoost Mulders 	va_end(args);
35892ca5b659SJoost Mulders 
35902ca5b659SJoost Mulders 	mutex_exit(prtdata.mutex);
35912ca5b659SJoost Mulders }
35922ca5b659SJoost Mulders 
35932ca5b659SJoost Mulders #if defined(DEBUG)
35942ca5b659SJoost Mulders static void
vr_prt(const char * fmt,...)35952ca5b659SJoost Mulders vr_prt(const char *fmt, ...)
35962ca5b659SJoost Mulders {
35972ca5b659SJoost Mulders 	va_list args;
35982ca5b659SJoost Mulders 
35992ca5b659SJoost Mulders 	ASSERT(mutex_owned(prtdata.mutex));
36002ca5b659SJoost Mulders 
36012ca5b659SJoost Mulders 	va_start(args, fmt);
36022ca5b659SJoost Mulders 	vr_vprt(fmt, args);
36032ca5b659SJoost Mulders 	va_end(args);
36042ca5b659SJoost Mulders 
36052ca5b659SJoost Mulders 	mutex_exit(prtdata.mutex);
36062ca5b659SJoost Mulders }
36072ca5b659SJoost Mulders 
36082ca5b659SJoost Mulders void
vr_debug()36092ca5b659SJoost Mulders (*vr_debug())(const char *fmt, ...)
36102ca5b659SJoost Mulders {
36112ca5b659SJoost Mulders 	mutex_enter(prtdata.mutex);
36122ca5b659SJoost Mulders 	prtdata.ifname = MODULENAME;
36132ca5b659SJoost Mulders 	prtdata.fmt = "^%s: %s\n";
36142ca5b659SJoost Mulders 	prtdata.level = CE_CONT;
36152ca5b659SJoost Mulders 
36162ca5b659SJoost Mulders 	return (vr_prt);
36172ca5b659SJoost Mulders }
36182ca5b659SJoost Mulders #endif	/* DEBUG */
36192ca5b659SJoost Mulders 
36202ca5b659SJoost Mulders DDI_DEFINE_STREAM_OPS(vr_dev_ops, nulldev, nulldev, vr_attach, vr_detach,
3621*cfe080a1SToomas Soome     nodev, NULL, D_MP, NULL, vr_quiesce);
36222ca5b659SJoost Mulders 
36232ca5b659SJoost Mulders static struct modldrv vr_modldrv = {
36242ca5b659SJoost Mulders 	&mod_driverops,		/* Type of module. This one is a driver */
36252ca5b659SJoost Mulders 	vr_ident,		/* short description */
36262ca5b659SJoost Mulders 	&vr_dev_ops		/* driver specific ops */
36272ca5b659SJoost Mulders };
36282ca5b659SJoost Mulders 
36292ca5b659SJoost Mulders static struct modlinkage modlinkage = {
36302ca5b659SJoost Mulders 	MODREV_1, (void *)&vr_modldrv, NULL
36312ca5b659SJoost Mulders };
36322ca5b659SJoost Mulders 
36332ca5b659SJoost Mulders int
_info(struct modinfo * modinfop)36342ca5b659SJoost Mulders _info(struct modinfo *modinfop)
36352ca5b659SJoost Mulders {
36362ca5b659SJoost Mulders 	return (mod_info(&modlinkage, modinfop));
36372ca5b659SJoost Mulders }
36382ca5b659SJoost Mulders 
36392ca5b659SJoost Mulders int
_init(void)36402ca5b659SJoost Mulders _init(void)
36412ca5b659SJoost Mulders {
36422ca5b659SJoost Mulders 	int	status;
36432ca5b659SJoost Mulders 
36442ca5b659SJoost Mulders 	mac_init_ops(&vr_dev_ops, MODULENAME);
36452ca5b659SJoost Mulders 	status = mod_install(&modlinkage);
36462ca5b659SJoost Mulders 	if (status == DDI_SUCCESS)
36472ca5b659SJoost Mulders 		mutex_init(prtdata.mutex, NULL, MUTEX_DRIVER, NULL);
36482ca5b659SJoost Mulders 	else
36492ca5b659SJoost Mulders 		mac_fini_ops(&vr_dev_ops);
36502ca5b659SJoost Mulders 	return (status);
36512ca5b659SJoost Mulders }
36522ca5b659SJoost Mulders 
36532ca5b659SJoost Mulders int
_fini(void)36542ca5b659SJoost Mulders _fini(void)
36552ca5b659SJoost Mulders {
36562ca5b659SJoost Mulders 	int status;
36572ca5b659SJoost Mulders 
36582ca5b659SJoost Mulders 	status = mod_remove(&modlinkage);
36592ca5b659SJoost Mulders 	if (status == 0) {
36602ca5b659SJoost Mulders 		mac_fini_ops(&vr_dev_ops);
36612ca5b659SJoost Mulders 		mutex_destroy(prtdata.mutex);
36622ca5b659SJoost Mulders 	}
36632ca5b659SJoost Mulders 	return (status);
36642ca5b659SJoost Mulders }
3665