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