xref: /illumos-gate/usr/src/uts/common/io/iprb/iprb.c (revision ab3f6e90)
182743679SGarrett D'Amore /*
282743679SGarrett D'Amore  * This file and its contents are supplied under the terms of the
382743679SGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
482743679SGarrett D'Amore  * You may only use this file in accordance with the terms of version
582743679SGarrett D'Amore  * 1.0 of the CDDL.
682743679SGarrett D'Amore  *
782743679SGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
882743679SGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet at
982743679SGarrett D'Amore  * http://www.illumos.org/license/CDDL.
1082743679SGarrett D'Amore  */
1182743679SGarrett D'Amore 
1282743679SGarrett D'Amore /*
13197c9523SMarcel Telka  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
149ca05893SAndy Fiddaman  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
15*ab3f6e90SJoshua M. Clulow  * Copyright 2022 Oxide Computer Company
1682743679SGarrett D'Amore  */
1782743679SGarrett D'Amore 
1882743679SGarrett D'Amore /*
1982743679SGarrett D'Amore  * Intel Pro/100B Ethernet Driver
2082743679SGarrett D'Amore  */
2182743679SGarrett D'Amore 
2282743679SGarrett D'Amore #include <sys/types.h>
2382743679SGarrett D'Amore #include <sys/modctl.h>
2482743679SGarrett D'Amore #include <sys/conf.h>
2582743679SGarrett D'Amore #include <sys/kmem.h>
2682743679SGarrett D'Amore #include <sys/ksynch.h>
2782743679SGarrett D'Amore #include <sys/cmn_err.h>
2882743679SGarrett D'Amore #include <sys/note.h>
2982743679SGarrett D'Amore #include <sys/pci.h>
3082743679SGarrett D'Amore #include <sys/pci_cap.h>
3182743679SGarrett D'Amore #include <sys/ethernet.h>
3282743679SGarrett D'Amore #include <sys/mii.h>
3382743679SGarrett D'Amore #include <sys/miiregs.h>
3482743679SGarrett D'Amore #include <sys/mac.h>
3582743679SGarrett D'Amore #include <sys/mac_ether.h>
3682743679SGarrett D'Amore #include <sys/ethernet.h>
3782743679SGarrett D'Amore #include <sys/vlan.h>
3882743679SGarrett D'Amore #include <sys/list.h>
3982743679SGarrett D'Amore #include <sys/sysmacros.h>
4082743679SGarrett D'Amore #include <sys/varargs.h>
4182743679SGarrett D'Amore #include <sys/stream.h>
4282743679SGarrett D'Amore #include <sys/strsun.h>
4382743679SGarrett D'Amore #include <sys/ddi.h>
4482743679SGarrett D'Amore #include <sys/sunddi.h>
4582743679SGarrett D'Amore 
4682743679SGarrett D'Amore #include "iprb.h"
4782743679SGarrett D'Amore #include "rcvbundl.h"
4882743679SGarrett D'Amore 
4982743679SGarrett D'Amore /*
5082743679SGarrett D'Amore  * Intel has openly documented the programming interface for these
5182743679SGarrett D'Amore  * parts in the "Intel 8255x 10/100 Mbps Ethernet Controller Family
5282743679SGarrett D'Amore  * Open Source Software Developer Manual".
5382743679SGarrett D'Amore  *
5482743679SGarrett D'Amore  * While some open source systems have utilized many of the features
5582743679SGarrett D'Amore  * of some models in this family (especially scatter gather and IP
5682743679SGarrett D'Amore  * checksum support), we have elected to offer only the basic
5782743679SGarrett D'Amore  * functionality.  These are only 10/100 parts, and the additional
5882743679SGarrett D'Amore  * complexity is not justified by the minimal performance benefit.
5982743679SGarrett D'Amore  * KISS.  So, we are only supporting the simple 82557 features.
6082743679SGarrett D'Amore  */
6182743679SGarrett D'Amore 
6282743679SGarrett D'Amore static uint16_t	iprb_mii_read(void *, uint8_t, uint8_t);
6382743679SGarrett D'Amore static void	iprb_mii_write(void *, uint8_t, uint8_t, uint16_t);
6482743679SGarrett D'Amore static void	iprb_mii_notify(void *, link_state_t);
6582743679SGarrett D'Amore static int	iprb_attach(dev_info_t *);
6682743679SGarrett D'Amore static int	iprb_detach(dev_info_t *);
6782743679SGarrett D'Amore static int	iprb_quiesce(dev_info_t *);
6882743679SGarrett D'Amore static int	iprb_suspend(dev_info_t *);
6982743679SGarrett D'Amore static int	iprb_resume(dev_info_t *);
7082743679SGarrett D'Amore static int	iprb_m_stat(void *, uint_t, uint64_t *);
7182743679SGarrett D'Amore static int	iprb_m_start(void *);
7282743679SGarrett D'Amore static void	iprb_m_stop(void *);
7382743679SGarrett D'Amore static int	iprb_m_promisc(void *, boolean_t);
7482743679SGarrett D'Amore static int	iprb_m_multicst(void *, boolean_t, const uint8_t *);
7582743679SGarrett D'Amore static int	iprb_m_unicst(void *, const uint8_t *);
7682743679SGarrett D'Amore static mblk_t	*iprb_m_tx(void *, mblk_t *);
7782743679SGarrett D'Amore static void	iprb_m_ioctl(void *, queue_t *, mblk_t *);
7882743679SGarrett D'Amore static int	iprb_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
7982743679SGarrett D'Amore     const void *);
8082743679SGarrett D'Amore static int	iprb_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
8182743679SGarrett D'Amore     void *);
8282743679SGarrett D'Amore static void	iprb_m_propinfo(void *, const char *, mac_prop_id_t,
8382743679SGarrett D'Amore     mac_prop_info_handle_t);
8482743679SGarrett D'Amore static void	iprb_destroy(iprb_t *);
8582743679SGarrett D'Amore static int	iprb_configure(iprb_t *);
8682743679SGarrett D'Amore static void	iprb_eeprom_sendbits(iprb_t *, uint32_t, uint8_t);
8782743679SGarrett D'Amore static uint16_t	iprb_eeprom_read(iprb_t *, uint16_t);
8882743679SGarrett D'Amore static void	iprb_identify(iprb_t *);
8982743679SGarrett D'Amore static int	iprb_cmd_submit(iprb_t *, uint16_t);
9082743679SGarrett D'Amore static void	iprb_cmd_reclaim(iprb_t *);
9182743679SGarrett D'Amore static int	iprb_cmd_ready(iprb_t *);
9282743679SGarrett D'Amore static int	iprb_cmd_drain(iprb_t *);
9382743679SGarrett D'Amore static void	iprb_rx_add(iprb_t *);
9482743679SGarrett D'Amore static void	iprb_rx_init(iprb_t *);
9582743679SGarrett D'Amore static mblk_t	*iprb_rx(iprb_t *);
9682743679SGarrett D'Amore static mblk_t	*iprb_send(iprb_t *, mblk_t *);
9782743679SGarrett D'Amore static uint_t	iprb_intr(caddr_t, caddr_t);
9882743679SGarrett D'Amore static void	iprb_periodic(void *);
9982743679SGarrett D'Amore static int	iprb_add_intr(iprb_t *);
10082743679SGarrett D'Amore static int	iprb_dma_alloc(iprb_t *, iprb_dma_t *, size_t);
10182743679SGarrett D'Amore static void	iprb_dma_free(iprb_dma_t *);
10282743679SGarrett D'Amore static iprb_dma_t *iprb_cmd_next(iprb_t *);
10382743679SGarrett D'Amore static int	iprb_set_config(iprb_t *);
10482743679SGarrett D'Amore static int	iprb_set_unicast(iprb_t *);
10582743679SGarrett D'Amore static int	iprb_set_multicast(iprb_t *);
10682743679SGarrett D'Amore static int	iprb_set_ucode(iprb_t *);
10782743679SGarrett D'Amore static void	iprb_update_stats(iprb_t *);
10882743679SGarrett D'Amore static int	iprb_start(iprb_t *);
10982743679SGarrett D'Amore static void	iprb_stop(iprb_t *);
11082743679SGarrett D'Amore static int	iprb_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
11182743679SGarrett D'Amore static int	iprb_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
11282743679SGarrett D'Amore static void	iprb_error(iprb_t *, const char *, ...);
11382743679SGarrett D'Amore 
11482743679SGarrett D'Amore static mii_ops_t iprb_mii_ops = {
11582743679SGarrett D'Amore 	MII_OPS_VERSION,
11682743679SGarrett D'Amore 	iprb_mii_read,
11782743679SGarrett D'Amore 	iprb_mii_write,
11882743679SGarrett D'Amore 	iprb_mii_notify,
11982743679SGarrett D'Amore 	NULL,		/* reset */
12082743679SGarrett D'Amore };
12182743679SGarrett D'Amore 
12282743679SGarrett D'Amore static mac_callbacks_t iprb_m_callbacks = {
12382743679SGarrett D'Amore 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
12482743679SGarrett D'Amore 	iprb_m_stat,
12582743679SGarrett D'Amore 	iprb_m_start,
12682743679SGarrett D'Amore 	iprb_m_stop,
12782743679SGarrett D'Amore 	iprb_m_promisc,
12882743679SGarrett D'Amore 	iprb_m_multicst,
12982743679SGarrett D'Amore 	iprb_m_unicst,
13082743679SGarrett D'Amore 	iprb_m_tx,
13182743679SGarrett D'Amore 	NULL,
13282743679SGarrett D'Amore 	iprb_m_ioctl,	/* mc_ioctl */
13382743679SGarrett D'Amore 	NULL,		/* mc_getcapab */
13482743679SGarrett D'Amore 	NULL,		/* mc_open */
13582743679SGarrett D'Amore 	NULL,		/* mc_close */
13682743679SGarrett D'Amore 	iprb_m_setprop,
13782743679SGarrett D'Amore 	iprb_m_getprop,
13882743679SGarrett D'Amore 	iprb_m_propinfo
13982743679SGarrett D'Amore };
14082743679SGarrett D'Amore 
14182743679SGarrett D'Amore 
14282743679SGarrett D'Amore /*
14382743679SGarrett D'Amore  * Stream information
14482743679SGarrett D'Amore  */
14582743679SGarrett D'Amore DDI_DEFINE_STREAM_OPS(iprb_devops, nulldev, nulldev,
14682743679SGarrett D'Amore     iprb_ddi_attach, iprb_ddi_detach, nodev, NULL, D_MP, NULL, iprb_quiesce);
14782743679SGarrett D'Amore 
14882743679SGarrett D'Amore static struct modldrv iprb_modldrv = {
14982743679SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
15082743679SGarrett D'Amore 	"Intel 8255x Ethernet",		/* drv_linkinfo */
15182743679SGarrett D'Amore 	&iprb_devops			/* drv_dev_ops */
15282743679SGarrett D'Amore };
15382743679SGarrett D'Amore 
15482743679SGarrett D'Amore static struct modlinkage iprb_modlinkage = {
15582743679SGarrett D'Amore 	MODREV_1,		/* ml_rev */
15682743679SGarrett D'Amore 	{ &iprb_modldrv, NULL }	/* ml_linkage */
15782743679SGarrett D'Amore };
15882743679SGarrett D'Amore 
15982743679SGarrett D'Amore 
16082743679SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = {
16182743679SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
16282743679SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
16382743679SGarrett D'Amore 	DDI_STRICTORDER_ACC
16482743679SGarrett D'Amore };
16582743679SGarrett D'Amore 
16682743679SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = {
16782743679SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
16882743679SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
16982743679SGarrett D'Amore 	DDI_STORECACHING_OK_ACC
17082743679SGarrett D'Amore };
17182743679SGarrett D'Amore 
17282743679SGarrett D'Amore /*
17382743679SGarrett D'Amore  * The 8225x is a 32-bit addressing engine, but it can only address up
17482743679SGarrett D'Amore  * to 31 bits on a single transaction.  (Far less in reality it turns
17582743679SGarrett D'Amore  * out.)  Statistics buffers have to be 16-byte aligned, and as we
17682743679SGarrett D'Amore  * allocate individual data pieces for other things, there is no
17782743679SGarrett D'Amore  * compelling reason to use another attribute with support for less
17882743679SGarrett D'Amore  * strict alignment.
17982743679SGarrett D'Amore  */
18082743679SGarrett D'Amore static ddi_dma_attr_t dma_attr = {
18182743679SGarrett D'Amore 	DMA_ATTR_V0,		/* dma_attr_version */
18282743679SGarrett D'Amore 	0,			/* dma_attr_addr_lo */
18382743679SGarrett D'Amore 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
18482743679SGarrett D'Amore 	0x7FFFFFFFU,		/* dma_attr_count_max */
18582743679SGarrett D'Amore 	16,			/* dma_attr_align */
18682743679SGarrett D'Amore 	0x100,			/* dma_attr_burstsizes */
18782743679SGarrett D'Amore 	1,			/* dma_attr_minxfer */
18882743679SGarrett D'Amore 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
18982743679SGarrett D'Amore 	0xFFFFFFFFU,		/* dma_attr_seg */
19082743679SGarrett D'Amore 	1,			/* dma_attr_sgllen */
19182743679SGarrett D'Amore 	1,			/* dma_attr_granular */
19282743679SGarrett D'Amore 	0			/* dma_attr_flags */
19382743679SGarrett D'Amore };
19482743679SGarrett D'Amore 
19582743679SGarrett D'Amore #define	DECL_UCODE(x)						\
19682743679SGarrett D'Amore 	static const uint32_t x ## _WORDS[] = x ## _RCVBUNDLE_UCODE
19782743679SGarrett D'Amore DECL_UCODE(D101_A);
19882743679SGarrett D'Amore DECL_UCODE(D101_B0);
19982743679SGarrett D'Amore DECL_UCODE(D101M_B);
20082743679SGarrett D'Amore DECL_UCODE(D101S);
20182743679SGarrett D'Amore DECL_UCODE(D102_B);
20282743679SGarrett D'Amore DECL_UCODE(D102_C);
20382743679SGarrett D'Amore DECL_UCODE(D102_E);
20482743679SGarrett D'Amore 
20582743679SGarrett D'Amore static uint8_t iprb_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
20682743679SGarrett D'Amore 
20782743679SGarrett D'Amore /*
20882743679SGarrett D'Amore  * We don't bother allowing for tuning of the CPU saver algorithm.
20982743679SGarrett D'Amore  * The ucode has reasonable defaults built-in.  However, some variants
21082743679SGarrett D'Amore  * apparently have bug fixes delivered via this ucode, so we still
21182743679SGarrett D'Amore  * need to support the ucode upload.
21282743679SGarrett D'Amore  */
21382743679SGarrett D'Amore typedef struct {
21482743679SGarrett D'Amore 	uint8_t		rev;
21582743679SGarrett D'Amore 	uint8_t		length;
21682743679SGarrett D'Amore 	const uint32_t	*ucode;
21782743679SGarrett D'Amore } iprb_ucode_t;
21882743679SGarrett D'Amore 
21982743679SGarrett D'Amore #define	UCODE(x)						\
22082743679SGarrett D'Amore 	sizeof (x ## _WORDS) / sizeof (uint32_t), x ## _WORDS
22182743679SGarrett D'Amore 
22282743679SGarrett D'Amore static const iprb_ucode_t iprb_ucode[] = {
22382743679SGarrett D'Amore 	{ REV_82558_A4,	UCODE(D101_A) },
22482743679SGarrett D'Amore 	{ REV_82558_B0,	UCODE(D101_B0) },
22582743679SGarrett D'Amore 	{ REV_82559_A0,	UCODE(D101M_B) },
22682743679SGarrett D'Amore 	{ REV_82559S_A,	UCODE(D101S) },
22782743679SGarrett D'Amore 	{ REV_82550,	UCODE(D102_B) },
22882743679SGarrett D'Amore 	{ REV_82550_C,	UCODE(D102_C) },
22982743679SGarrett D'Amore 	{ REV_82551_F,	UCODE(D102_E) },
23082743679SGarrett D'Amore 	{ 0 },
23182743679SGarrett D'Amore };
23282743679SGarrett D'Amore 
23382743679SGarrett D'Amore int
_init(void)23482743679SGarrett D'Amore _init(void)
23582743679SGarrett D'Amore {
23682743679SGarrett D'Amore 	int	rv;
23782743679SGarrett D'Amore 	mac_init_ops(&iprb_devops, "iprb");
23882743679SGarrett D'Amore 	if ((rv = mod_install(&iprb_modlinkage)) != DDI_SUCCESS) {
23982743679SGarrett D'Amore 		mac_fini_ops(&iprb_devops);
24082743679SGarrett D'Amore 	}
24182743679SGarrett D'Amore 	return (rv);
24282743679SGarrett D'Amore }
24382743679SGarrett D'Amore 
24482743679SGarrett D'Amore int
_fini(void)24582743679SGarrett D'Amore _fini(void)
24682743679SGarrett D'Amore {
24782743679SGarrett D'Amore 	int	rv;
24882743679SGarrett D'Amore 	if ((rv = mod_remove(&iprb_modlinkage)) == DDI_SUCCESS) {
24982743679SGarrett D'Amore 		mac_fini_ops(&iprb_devops);
25082743679SGarrett D'Amore 	}
25182743679SGarrett D'Amore 	return (rv);
25282743679SGarrett D'Amore }
25382743679SGarrett D'Amore 
25482743679SGarrett D'Amore int
_info(struct modinfo * modinfop)25582743679SGarrett D'Amore _info(struct modinfo *modinfop)
25682743679SGarrett D'Amore {
25782743679SGarrett D'Amore 	return (mod_info(&iprb_modlinkage, modinfop));
25882743679SGarrett D'Amore }
25982743679SGarrett D'Amore 
26082743679SGarrett D'Amore int
iprb_attach(dev_info_t * dip)26182743679SGarrett D'Amore iprb_attach(dev_info_t *dip)
26282743679SGarrett D'Amore {
26382743679SGarrett D'Amore 	iprb_t		*ip;
26482743679SGarrett D'Amore 	uint16_t	w;
26582743679SGarrett D'Amore 	int		i;
26682743679SGarrett D'Amore 	mac_register_t	*macp;
26782743679SGarrett D'Amore 
26882743679SGarrett D'Amore 	ip = kmem_zalloc(sizeof (*ip), KM_SLEEP);
26982743679SGarrett D'Amore 	ddi_set_driver_private(dip, ip);
27082743679SGarrett D'Amore 	ip->dip = dip;
27182743679SGarrett D'Amore 
27282743679SGarrett D'Amore 	list_create(&ip->mcast, sizeof (struct iprb_mcast),
27382743679SGarrett D'Amore 	    offsetof(struct iprb_mcast, node));
27482743679SGarrett D'Amore 
27582743679SGarrett D'Amore 	/* we don't support high level interrupts, so we don't need cookies */
27682743679SGarrett D'Amore 	mutex_init(&ip->culock, NULL, MUTEX_DRIVER, NULL);
27782743679SGarrett D'Amore 	mutex_init(&ip->rulock, NULL, MUTEX_DRIVER, NULL);
27882743679SGarrett D'Amore 
27982743679SGarrett D'Amore 	if (pci_config_setup(dip, &ip->pcih) != DDI_SUCCESS) {
28082743679SGarrett D'Amore 		iprb_error(ip, "unable to map configuration space");
28182743679SGarrett D'Amore 		iprb_destroy(ip);
28282743679SGarrett D'Amore 		return (DDI_FAILURE);
28382743679SGarrett D'Amore 	}
28482743679SGarrett D'Amore 
28582743679SGarrett D'Amore 	if (ddi_regs_map_setup(dip, 1, &ip->regs, 0, 0, &acc_attr,
28682743679SGarrett D'Amore 	    &ip->regsh) != DDI_SUCCESS) {
28782743679SGarrett D'Amore 		iprb_error(ip, "unable to map device registers");
28882743679SGarrett D'Amore 		iprb_destroy(ip);
28982743679SGarrett D'Amore 		return (DDI_FAILURE);
29082743679SGarrett D'Amore 	}
29182743679SGarrett D'Amore 
29282743679SGarrett D'Amore 	/* Reset, but first go into idle state */
29382743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SEL_RESET);
29482743679SGarrett D'Amore 	drv_usecwait(10);
29582743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SW_RESET);
29682743679SGarrett D'Amore 	drv_usecwait(10);
29782743679SGarrett D'Amore 	PUT8(ip, CSR_INTCTL, INTCTL_MASK);
29882743679SGarrett D'Amore 	(void) GET8(ip, CSR_INTCTL);
29982743679SGarrett D'Amore 
30082743679SGarrett D'Amore 	/*
30182743679SGarrett D'Amore 	 * Precalculate watchdog times.
30282743679SGarrett D'Amore 	 */
3030529d5c6SJosef 'Jeff' Sipek 	ip->tx_timeout = TX_WATCHDOG;
3040529d5c6SJosef 'Jeff' Sipek 	ip->rx_timeout = RX_WATCHDOG;
30582743679SGarrett D'Amore 
30682743679SGarrett D'Amore 	iprb_identify(ip);
30782743679SGarrett D'Amore 
30882743679SGarrett D'Amore 	/* Obtain our factory MAC address */
30982743679SGarrett D'Amore 	w = iprb_eeprom_read(ip, 0);
31082743679SGarrett D'Amore 	ip->factaddr[0] = w & 0xff;
31182743679SGarrett D'Amore 	ip->factaddr[1] = w >> 8;
31282743679SGarrett D'Amore 	w = iprb_eeprom_read(ip, 1);
31382743679SGarrett D'Amore 	ip->factaddr[2] = w & 0xff;
31482743679SGarrett D'Amore 	ip->factaddr[3] = w >> 8;
31582743679SGarrett D'Amore 	w = iprb_eeprom_read(ip, 2);
31682743679SGarrett D'Amore 	ip->factaddr[4] = w & 0xff;
31782743679SGarrett D'Amore 	ip->factaddr[5] = w >> 8;
31882743679SGarrett D'Amore 	bcopy(ip->factaddr, ip->curraddr, 6);
31982743679SGarrett D'Amore 
32082743679SGarrett D'Amore 	if (ip->resumebug) {
32182743679SGarrett D'Amore 		/*
32282743679SGarrett D'Amore 		 * Generally, most devices we will ever see will
32382743679SGarrett D'Amore 		 * already have fixed firmware.  Since I can't verify
32482743679SGarrett D'Amore 		 * the validity of the fix (no suitably downrev
32582743679SGarrett D'Amore 		 * hardware), we'll just do our best to avoid it for
32682743679SGarrett D'Amore 		 * devices that exhibit this behavior.
32782743679SGarrett D'Amore 		 */
32882743679SGarrett D'Amore 		if ((iprb_eeprom_read(ip, 10) & 0x02) == 0) {
32982743679SGarrett D'Amore 			/* EEPROM fix was already applied, assume safe. */
33082743679SGarrett D'Amore 			ip->resumebug = B_FALSE;
33182743679SGarrett D'Amore 		}
33282743679SGarrett D'Amore 	}
33382743679SGarrett D'Amore 
33482743679SGarrett D'Amore 	if ((iprb_eeprom_read(ip, 3) & 0x3) != 0x3) {
33582743679SGarrett D'Amore 		cmn_err(CE_CONT, "?Enabling RX errata workaround.\n");
33682743679SGarrett D'Amore 		ip->rxhangbug = B_TRUE;
33782743679SGarrett D'Amore 	}
33882743679SGarrett D'Amore 
33982743679SGarrett D'Amore 	/* Determine whether we have an MII or a legacy 80c24 */
34082743679SGarrett D'Amore 	w = iprb_eeprom_read(ip, 6);
34182743679SGarrett D'Amore 	if ((w & 0x3f00) != 0x0600) {
34282743679SGarrett D'Amore 		if ((ip->miih = mii_alloc(ip, dip, &iprb_mii_ops)) == NULL) {
34382743679SGarrett D'Amore 			iprb_error(ip, "unable to allocate MII ops vector");
34482743679SGarrett D'Amore 			iprb_destroy(ip);
34582743679SGarrett D'Amore 			return (DDI_FAILURE);
34682743679SGarrett D'Amore 		}
34782743679SGarrett D'Amore 		if (ip->canpause) {
34882743679SGarrett D'Amore 			mii_set_pauseable(ip->miih, B_TRUE, B_FALSE);
34982743679SGarrett D'Amore 		}
35082743679SGarrett D'Amore 	}
35182743679SGarrett D'Amore 
35282743679SGarrett D'Amore 	/* Allocate cmds and tx region */
35382743679SGarrett D'Amore 	for (i = 0; i < NUM_TX; i++) {
35482743679SGarrett D'Amore 		/* Command blocks */
35582743679SGarrett D'Amore 		if (iprb_dma_alloc(ip, &ip->cmds[i], CB_SIZE) != DDI_SUCCESS) {
35682743679SGarrett D'Amore 			iprb_destroy(ip);
35782743679SGarrett D'Amore 			return (DDI_FAILURE);
35882743679SGarrett D'Amore 		}
35982743679SGarrett D'Amore 	}
36082743679SGarrett D'Amore 
36182743679SGarrett D'Amore 	for (i = 0; i < NUM_TX; i++) {
36282743679SGarrett D'Amore 		iprb_dma_t *cb = &ip->cmds[i];
36382743679SGarrett D'Amore 		/* Link the command blocks into a ring */
36482743679SGarrett D'Amore 		PUTCB32(cb, CB_LNK_OFFSET, (ip->cmds[(i + 1) % NUM_TX].paddr));
36582743679SGarrett D'Amore 	}
36682743679SGarrett D'Amore 
36782743679SGarrett D'Amore 	for (i = 0; i < NUM_RX; i++) {
36882743679SGarrett D'Amore 		/* Rx packet buffers */
36982743679SGarrett D'Amore 		if (iprb_dma_alloc(ip, &ip->rxb[i], RFD_SIZE) != DDI_SUCCESS) {
37082743679SGarrett D'Amore 			iprb_destroy(ip);
37182743679SGarrett D'Amore 			return (DDI_FAILURE);
37282743679SGarrett D'Amore 		}
37382743679SGarrett D'Amore 	}
37482743679SGarrett D'Amore 	if (iprb_dma_alloc(ip, &ip->stats, STATS_SIZE) != DDI_SUCCESS) {
37582743679SGarrett D'Amore 		iprb_destroy(ip);
37682743679SGarrett D'Amore 		return (DDI_FAILURE);
37782743679SGarrett D'Amore 	}
37882743679SGarrett D'Amore 
37982743679SGarrett D'Amore 	if (iprb_add_intr(ip) != DDI_SUCCESS) {
38082743679SGarrett D'Amore 		iprb_destroy(ip);
38182743679SGarrett D'Amore 		return (DDI_FAILURE);
38282743679SGarrett D'Amore 	}
38382743679SGarrett D'Amore 
38482743679SGarrett D'Amore 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
38582743679SGarrett D'Amore 		iprb_error(ip, "unable to allocate mac structure");
38682743679SGarrett D'Amore 		iprb_destroy(ip);
38782743679SGarrett D'Amore 		return (DDI_FAILURE);
38882743679SGarrett D'Amore 	}
38982743679SGarrett D'Amore 
39082743679SGarrett D'Amore 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
39182743679SGarrett D'Amore 	macp->m_driver = ip;
39282743679SGarrett D'Amore 	macp->m_dip = dip;
39382743679SGarrett D'Amore 	macp->m_src_addr = ip->curraddr;
39482743679SGarrett D'Amore 	macp->m_callbacks = &iprb_m_callbacks;
39582743679SGarrett D'Amore 	macp->m_min_sdu = 0;
39682743679SGarrett D'Amore 	macp->m_max_sdu = ETHERMTU;
39782743679SGarrett D'Amore 	macp->m_margin = VLAN_TAGSZ;
39882743679SGarrett D'Amore 	if (mac_register(macp, &ip->mach) != 0) {
39982743679SGarrett D'Amore 		iprb_error(ip, "unable to register mac with framework");
40082743679SGarrett D'Amore 		mac_free(macp);
40182743679SGarrett D'Amore 		iprb_destroy(ip);
40282743679SGarrett D'Amore 		return (DDI_FAILURE);
40382743679SGarrett D'Amore 	}
40482743679SGarrett D'Amore 
40582743679SGarrett D'Amore 	mac_free(macp);
40682743679SGarrett D'Amore 	return (DDI_SUCCESS);
40782743679SGarrett D'Amore }
40882743679SGarrett D'Amore 
40982743679SGarrett D'Amore int
iprb_detach(dev_info_t * dip)41082743679SGarrett D'Amore iprb_detach(dev_info_t *dip)
41182743679SGarrett D'Amore {
41282743679SGarrett D'Amore 	iprb_t *ip;
41382743679SGarrett D'Amore 
41482743679SGarrett D'Amore 	ip = ddi_get_driver_private(dip);
41582743679SGarrett D'Amore 	ASSERT(ip != NULL);
41682743679SGarrett D'Amore 
41782743679SGarrett D'Amore 	if (mac_disable(ip->mach) != 0)
41882743679SGarrett D'Amore 		return (DDI_FAILURE);
41982743679SGarrett D'Amore 
42082743679SGarrett D'Amore 	(void) mac_unregister(ip->mach);
42182743679SGarrett D'Amore 	iprb_destroy(ip);
42282743679SGarrett D'Amore 	return (DDI_SUCCESS);
42382743679SGarrett D'Amore }
42482743679SGarrett D'Amore 
42582743679SGarrett D'Amore int
iprb_add_intr(iprb_t * ip)42682743679SGarrett D'Amore iprb_add_intr(iprb_t *ip)
42782743679SGarrett D'Amore {
42882743679SGarrett D'Amore 	int	actual;
42982743679SGarrett D'Amore 
43082743679SGarrett D'Amore 	if (ddi_intr_alloc(ip->dip, &ip->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
43182743679SGarrett D'Amore 	    &actual, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
43282743679SGarrett D'Amore 		iprb_error(ip, "failed allocating interrupt handle");
43382743679SGarrett D'Amore 		return (DDI_FAILURE);
43482743679SGarrett D'Amore 	}
43582743679SGarrett D'Amore 
43682743679SGarrett D'Amore 	if (ddi_intr_add_handler(ip->intrh, iprb_intr, ip, NULL) !=
43782743679SGarrett D'Amore 	    DDI_SUCCESS) {
43882743679SGarrett D'Amore 		(void) ddi_intr_free(ip->intrh);
43982743679SGarrett D'Amore 		ip->intrh = NULL;
44082743679SGarrett D'Amore 		iprb_error(ip, "failed adding interrupt handler");
44182743679SGarrett D'Amore 		return (DDI_FAILURE);
44282743679SGarrett D'Amore 	}
44382743679SGarrett D'Amore 	if (ddi_intr_enable(ip->intrh) != DDI_SUCCESS) {
44482743679SGarrett D'Amore 		(void) ddi_intr_remove_handler(ip->intrh);
44582743679SGarrett D'Amore 		(void) ddi_intr_free(ip->intrh);
44682743679SGarrett D'Amore 		ip->intrh = NULL;
44782743679SGarrett D'Amore 		iprb_error(ip, "failed enabling interrupt");
44882743679SGarrett D'Amore 		return (DDI_FAILURE);
44982743679SGarrett D'Amore 	}
45082743679SGarrett D'Amore 	return (DDI_SUCCESS);
45182743679SGarrett D'Amore }
45282743679SGarrett D'Amore 
45382743679SGarrett D'Amore int
iprb_dma_alloc(iprb_t * ip,iprb_dma_t * h,size_t size)45482743679SGarrett D'Amore iprb_dma_alloc(iprb_t *ip, iprb_dma_t *h, size_t size)
45582743679SGarrett D'Amore {
45682743679SGarrett D'Amore 	size_t			rlen;
45782743679SGarrett D'Amore 	ddi_dma_cookie_t	dmac;
45882743679SGarrett D'Amore 	uint_t			ndmac;
45982743679SGarrett D'Amore 
46082743679SGarrett D'Amore 	if (ddi_dma_alloc_handle(ip->dip, &dma_attr, DDI_DMA_SLEEP, NULL,
46182743679SGarrett D'Amore 	    &h->dmah) != DDI_SUCCESS) {
46282743679SGarrett D'Amore 		iprb_error(ip, "unable to allocate dma handle");
46382743679SGarrett D'Amore 		return (DDI_FAILURE);
46482743679SGarrett D'Amore 	}
46582743679SGarrett D'Amore 	if (ddi_dma_mem_alloc(h->dmah, size, &buf_attr, DDI_DMA_CONSISTENT,
46682743679SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &h->vaddr, &rlen, &h->acch) != DDI_SUCCESS) {
46782743679SGarrett D'Amore 		iprb_error(ip, "unable to allocate dma memory");
46882743679SGarrett D'Amore 		return (DDI_FAILURE);
46982743679SGarrett D'Amore 	}
47082743679SGarrett D'Amore 	bzero(h->vaddr, size);
47182743679SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(h->dmah, NULL, h->vaddr, size,
47282743679SGarrett D'Amore 	    DDI_DMA_CONSISTENT | DDI_DMA_RDWR, DDI_DMA_SLEEP, NULL,
47382743679SGarrett D'Amore 	    &dmac, &ndmac) != DDI_DMA_MAPPED) {
47482743679SGarrett D'Amore 		iprb_error(ip, "unable to map command memory");
47582743679SGarrett D'Amore 		return (DDI_FAILURE);
47682743679SGarrett D'Amore 	}
47782743679SGarrett D'Amore 	h->paddr = dmac.dmac_address;
47882743679SGarrett D'Amore 	return (DDI_SUCCESS);
47982743679SGarrett D'Amore }
48082743679SGarrett D'Amore 
48182743679SGarrett D'Amore void
iprb_dma_free(iprb_dma_t * h)48282743679SGarrett D'Amore iprb_dma_free(iprb_dma_t *h)
48382743679SGarrett D'Amore {
48482743679SGarrett D'Amore 	if (h->paddr != 0)
48582743679SGarrett D'Amore 		(void) ddi_dma_unbind_handle(h->dmah);
48682743679SGarrett D'Amore 	h->paddr = 0;
48782743679SGarrett D'Amore 	if (h->acch != NULL)
48882743679SGarrett D'Amore 		ddi_dma_mem_free(&h->acch);
48982743679SGarrett D'Amore 	h->acch = NULL;
49082743679SGarrett D'Amore 	if (h->dmah != NULL)
49182743679SGarrett D'Amore 		ddi_dma_free_handle(&h->dmah);
49282743679SGarrett D'Amore 	h->dmah = NULL;
49382743679SGarrett D'Amore }
49482743679SGarrett D'Amore 
49582743679SGarrett D'Amore void
iprb_destroy(iprb_t * ip)49682743679SGarrett D'Amore iprb_destroy(iprb_t *ip)
49782743679SGarrett D'Amore {
49882743679SGarrett D'Amore 	int i;
49982743679SGarrett D'Amore 	iprb_mcast_t *mc;
50082743679SGarrett D'Amore 
50182743679SGarrett D'Amore 	/* shut down interrupts */
50282743679SGarrett D'Amore 	if (ip->intrh != NULL) {
50382743679SGarrett D'Amore 		(void) ddi_intr_disable(ip->intrh);
50482743679SGarrett D'Amore 		(void) ddi_intr_remove_handler(ip->intrh);
50582743679SGarrett D'Amore 		(void) ddi_intr_free(ip->intrh);
50682743679SGarrett D'Amore 	}
50782743679SGarrett D'Amore 	/* release DMA resources */
50882743679SGarrett D'Amore 	for (i = 0; i < NUM_TX; i++) {
50982743679SGarrett D'Amore 		iprb_dma_free(&ip->cmds[i]);
51082743679SGarrett D'Amore 	}
51182743679SGarrett D'Amore 	for (i = 0; i < NUM_RX; i++) {
51282743679SGarrett D'Amore 		iprb_dma_free(&ip->rxb[i]);
51382743679SGarrett D'Amore 	}
51482743679SGarrett D'Amore 	iprb_dma_free(&ip->stats);
51582743679SGarrett D'Amore 
51682743679SGarrett D'Amore 	if (ip->miih)
51782743679SGarrett D'Amore 		mii_free(ip->miih);
51882743679SGarrett D'Amore 
51982743679SGarrett D'Amore 	/* clean up the multicast list */
52082743679SGarrett D'Amore 	while ((mc = list_head(&ip->mcast)) != NULL) {
52182743679SGarrett D'Amore 		list_remove(&ip->mcast, mc);
52282743679SGarrett D'Amore 		kmem_free(mc, sizeof (*mc));
52382743679SGarrett D'Amore 	}
52482743679SGarrett D'Amore 
52582743679SGarrett D'Amore 	/* tear down register mappings */
52682743679SGarrett D'Amore 	if (ip->pcih)
52782743679SGarrett D'Amore 		pci_config_teardown(&ip->pcih);
52882743679SGarrett D'Amore 	if (ip->regsh)
52982743679SGarrett D'Amore 		ddi_regs_map_free(&ip->regsh);
53082743679SGarrett D'Amore 
53182743679SGarrett D'Amore 	/* clean the dip */
53282743679SGarrett D'Amore 	ddi_set_driver_private(ip->dip, NULL);
53382743679SGarrett D'Amore 
53482743679SGarrett D'Amore 	list_destroy(&ip->mcast);
53582743679SGarrett D'Amore 	mutex_destroy(&ip->culock);
53682743679SGarrett D'Amore 	mutex_destroy(&ip->rulock);
53782743679SGarrett D'Amore 
53882743679SGarrett D'Amore 	/* and finally toss the structure itself */
53982743679SGarrett D'Amore 	kmem_free(ip, sizeof (*ip));
54082743679SGarrett D'Amore }
54182743679SGarrett D'Amore 
54282743679SGarrett D'Amore void
iprb_identify(iprb_t * ip)54382743679SGarrett D'Amore iprb_identify(iprb_t *ip)
54482743679SGarrett D'Amore {
54582743679SGarrett D'Amore 	ip->devid = pci_config_get16(ip->pcih, PCI_CONF_DEVID);
54682743679SGarrett D'Amore 	ip->revid = pci_config_get8(ip->pcih, PCI_CONF_REVID);
54782743679SGarrett D'Amore 
54882743679SGarrett D'Amore 	switch (ip->devid) {
54982743679SGarrett D'Amore 	case 0x1229:	/* 8255x family */
55082743679SGarrett D'Amore 	case 0x1030:	/* Intel InBusiness */
55182743679SGarrett D'Amore 
55282743679SGarrett D'Amore 		if (ip->revid >= REV_82558_A4) {
55382743679SGarrett D'Amore 			ip->canpause = B_TRUE;
55482743679SGarrett D'Amore 			ip->canmwi = B_TRUE;
55582743679SGarrett D'Amore 		} else {
55682743679SGarrett D'Amore 			ip->is557 = B_TRUE;
55782743679SGarrett D'Amore 		}
55882743679SGarrett D'Amore 		if (ip->revid >= REV_82559_A0)
55982743679SGarrett D'Amore 			ip->resumebug = B_TRUE;
56082743679SGarrett D'Amore 		break;
56182743679SGarrett D'Amore 
56282743679SGarrett D'Amore 	case 0x1209:	/* Embedded 82559ER */
56382743679SGarrett D'Amore 		ip->canpause = B_TRUE;
56482743679SGarrett D'Amore 		ip->resumebug = B_TRUE;
56582743679SGarrett D'Amore 		ip->canmwi = B_TRUE;
56682743679SGarrett D'Amore 		break;
56782743679SGarrett D'Amore 
56882743679SGarrett D'Amore 	case 0x2449:	/* ICH2 */
56982743679SGarrett D'Amore 	case 0x1031:	/* Pro/100 VE (ICH3) */
57082743679SGarrett D'Amore 	case 0x1032:	/* Pro/100 VE (ICH3) */
57182743679SGarrett D'Amore 	case 0x1033:	/* Pro/100 VM (ICH3) */
57282743679SGarrett D'Amore 	case 0x1034:	/* Pro/100 VM (ICH3) */
57382743679SGarrett D'Amore 	case 0x1038:	/* Pro/100 VM (ICH3) */
57482743679SGarrett D'Amore 		ip->resumebug = B_TRUE;
57582743679SGarrett D'Amore 		if (ip->revid >= REV_82558_A4)
57682743679SGarrett D'Amore 			ip->canpause = B_TRUE;
57782743679SGarrett D'Amore 		break;
57882743679SGarrett D'Amore 
57982743679SGarrett D'Amore 	default:
58082743679SGarrett D'Amore 		if (ip->revid >= REV_82558_A4)
58182743679SGarrett D'Amore 			ip->canpause = B_TRUE;
58282743679SGarrett D'Amore 		break;
58382743679SGarrett D'Amore 	}
58482743679SGarrett D'Amore 
58582743679SGarrett D'Amore 	/* Allow property override MWI support - not normally needed. */
58682743679SGarrett D'Amore 	if (ddi_prop_get_int(DDI_DEV_T_ANY, ip->dip, 0, "MWIEnable", 1) == 0) {
58782743679SGarrett D'Amore 		ip->canmwi = B_FALSE;
58882743679SGarrett D'Amore 	}
58982743679SGarrett D'Amore }
59082743679SGarrett D'Amore 
59182743679SGarrett D'Amore void
iprb_eeprom_sendbits(iprb_t * ip,uint32_t val,uint8_t nbits)59282743679SGarrett D'Amore iprb_eeprom_sendbits(iprb_t *ip, uint32_t val, uint8_t nbits)
59382743679SGarrett D'Amore {
59482743679SGarrett D'Amore 	uint32_t	mask;
59582743679SGarrett D'Amore 	uint16_t	x;
59682743679SGarrett D'Amore 
59782743679SGarrett D'Amore 	mask = 1U << (nbits - 1);
59882743679SGarrett D'Amore 	while (mask) {
59982743679SGarrett D'Amore 		x = (mask & val) ? EEPROM_EEDI : 0;
60082743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EECS);
60182743679SGarrett D'Amore 		drv_usecwait(100);
60282743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS);
60382743679SGarrett D'Amore 		drv_usecwait(100);
60482743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EECS);
60582743679SGarrett D'Amore 		drv_usecwait(100);
60682743679SGarrett D'Amore 		mask >>= 1;
60782743679SGarrett D'Amore 	}
60882743679SGarrett D'Amore }
60982743679SGarrett D'Amore 
61082743679SGarrett D'Amore uint16_t
iprb_eeprom_read(iprb_t * ip,uint16_t address)61182743679SGarrett D'Amore iprb_eeprom_read(iprb_t *ip, uint16_t address)
61282743679SGarrett D'Amore {
61382743679SGarrett D'Amore 	uint16_t	val;
61482743679SGarrett D'Amore 	int		mask;
61582743679SGarrett D'Amore 	uint16_t	n;
61682743679SGarrett D'Amore 	uint16_t	bits;
61782743679SGarrett D'Amore 
61882743679SGarrett D'Amore 	/* if we don't know the address size yet call again to determine it */
61982743679SGarrett D'Amore 	if ((address != 0) && (ip->eeprom_bits == 0))
62082743679SGarrett D'Amore 		(void) iprb_eeprom_read(ip, 0);
62182743679SGarrett D'Amore 
62282743679SGarrett D'Amore 	if ((bits = ip->eeprom_bits) == 0) {
62382743679SGarrett D'Amore 		bits = 8;
62482743679SGarrett D'Amore 		ASSERT(address == 0);
62582743679SGarrett D'Amore 	}
62682743679SGarrett D'Amore 	/* enable the EEPROM chip select */
62782743679SGarrett D'Amore 	PUT16(ip, CSR_EECTL, EEPROM_EECS);
62882743679SGarrett D'Amore 	drv_usecwait(100);
62982743679SGarrett D'Amore 
63082743679SGarrett D'Amore 	/* send a read command */
63182743679SGarrett D'Amore 	iprb_eeprom_sendbits(ip, 6, 3);
63282743679SGarrett D'Amore 	n = 0;
63382743679SGarrett D'Amore 	for (mask = (1U << (bits - 1)); mask != 0; mask >>= 1) {
63482743679SGarrett D'Amore 		uint16_t x = (mask & address) ? EEPROM_EEDI : 0;
63582743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EECS);
63682743679SGarrett D'Amore 		drv_usecwait(100);
63782743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EESK | EEPROM_EECS);
63882743679SGarrett D'Amore 		drv_usecwait(100);
63982743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, x | EEPROM_EECS);
64082743679SGarrett D'Amore 		drv_usecwait(100);
64182743679SGarrett D'Amore 
64282743679SGarrett D'Amore 		n++;
64382743679SGarrett D'Amore 		/* check the dummy 0 bit */
64482743679SGarrett D'Amore 		if ((GET16(ip, CSR_EECTL) & EEPROM_EEDO) == 0) {
64582743679SGarrett D'Amore 			if (ip->eeprom_bits == 0) {
64682743679SGarrett D'Amore 				ip->eeprom_bits = n;
64782743679SGarrett D'Amore 				cmn_err(CE_CONT, "?EEPROM size %d words.\n",
64882743679SGarrett D'Amore 				    1U << ip->eeprom_bits);
64982743679SGarrett D'Amore 			}
65082743679SGarrett D'Amore 			break;
65182743679SGarrett D'Amore 		}
65282743679SGarrett D'Amore 	}
65382743679SGarrett D'Amore 	if (n != ip->eeprom_bits) {
65482743679SGarrett D'Amore 		iprb_error(ip, "cannot determine EEPROM size (%d, %d)",
65582743679SGarrett D'Amore 		    ip->eeprom_bits, n);
65682743679SGarrett D'Amore 	}
65782743679SGarrett D'Amore 
65882743679SGarrett D'Amore 	/* shift out a 16-bit word */
65982743679SGarrett D'Amore 	val = 0;
66082743679SGarrett D'Amore 	for (mask = 0x8000; mask; mask >>= 1) {
66182743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, EEPROM_EECS | EEPROM_EESK);
66282743679SGarrett D'Amore 		drv_usecwait(100);
66382743679SGarrett D'Amore 		if (GET16(ip, CSR_EECTL) & EEPROM_EEDO)
66482743679SGarrett D'Amore 			val |= mask;
66582743679SGarrett D'Amore 		drv_usecwait(100);
66682743679SGarrett D'Amore 		PUT16(ip, CSR_EECTL, EEPROM_EECS);
66782743679SGarrett D'Amore 		drv_usecwait(100);
66882743679SGarrett D'Amore 	}
66982743679SGarrett D'Amore 
67082743679SGarrett D'Amore 	/* and disable the eeprom */
67182743679SGarrett D'Amore 	PUT16(ip, CSR_EECTL, 0);
67282743679SGarrett D'Amore 	drv_usecwait(100);
67382743679SGarrett D'Amore 
67482743679SGarrett D'Amore 	return (val);
67582743679SGarrett D'Amore }
67682743679SGarrett D'Amore 
67782743679SGarrett D'Amore int
iprb_cmd_ready(iprb_t * ip)67882743679SGarrett D'Amore iprb_cmd_ready(iprb_t *ip)
67982743679SGarrett D'Amore {
68082743679SGarrett D'Amore 	/* wait for pending SCB commands to be accepted */
68182743679SGarrett D'Amore 	for (int cnt = 1000000; cnt != 0; cnt -= 10) {
68282743679SGarrett D'Amore 		if (GET8(ip, CSR_CMD) == 0) {
68382743679SGarrett D'Amore 			return (DDI_SUCCESS);
68482743679SGarrett D'Amore 		}
68582743679SGarrett D'Amore 		drv_usecwait(10);
68682743679SGarrett D'Amore 	}
68782743679SGarrett D'Amore 	iprb_error(ip, "timeout waiting for chip to become ready");
68882743679SGarrett D'Amore 	return (DDI_FAILURE);
68982743679SGarrett D'Amore }
69082743679SGarrett D'Amore 
69182743679SGarrett D'Amore void
iprb_cmd_reclaim(iprb_t * ip)69282743679SGarrett D'Amore iprb_cmd_reclaim(iprb_t *ip)
69382743679SGarrett D'Amore {
69482743679SGarrett D'Amore 	while (ip->cmd_count) {
69582743679SGarrett D'Amore 		iprb_dma_t *cb = &ip->cmds[ip->cmd_tail];
69682743679SGarrett D'Amore 
69782743679SGarrett D'Amore 		SYNCCB(cb, CB_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
69882743679SGarrett D'Amore 		if ((GETCB16(cb, CB_STS_OFFSET) & CB_STS_C) == 0) {
69982743679SGarrett D'Amore 			break;
70082743679SGarrett D'Amore 		}
70182743679SGarrett D'Amore 
70282743679SGarrett D'Amore 		ip->cmd_tail++;
70382743679SGarrett D'Amore 		ip->cmd_tail %= NUM_TX;
70482743679SGarrett D'Amore 		ip->cmd_count--;
70582743679SGarrett D'Amore 		if (ip->cmd_count == 0) {
70682743679SGarrett D'Amore 			ip->tx_wdog = 0;
70782743679SGarrett D'Amore 		} else {
7080529d5c6SJosef 'Jeff' Sipek 			ip->tx_wdog = gethrtime();
70982743679SGarrett D'Amore 		}
71082743679SGarrett D'Amore 	}
71182743679SGarrett D'Amore }
71282743679SGarrett D'Amore 
71382743679SGarrett D'Amore int
iprb_cmd_drain(iprb_t * ip)71482743679SGarrett D'Amore iprb_cmd_drain(iprb_t *ip)
71582743679SGarrett D'Amore {
71682743679SGarrett D'Amore 	for (int i = 1000000; i; i -= 10) {
71782743679SGarrett D'Amore 		iprb_cmd_reclaim(ip);
71882743679SGarrett D'Amore 		if (ip->cmd_count == 0)
71982743679SGarrett D'Amore 			return (DDI_SUCCESS);
72082743679SGarrett D'Amore 		drv_usecwait(10);
72182743679SGarrett D'Amore 	}
72282743679SGarrett D'Amore 	iprb_error(ip, "time out waiting for commands to drain");
72382743679SGarrett D'Amore 	return (DDI_FAILURE);
72482743679SGarrett D'Amore }
72582743679SGarrett D'Amore 
72682743679SGarrett D'Amore int
iprb_cmd_submit(iprb_t * ip,uint16_t cmd)72782743679SGarrett D'Amore iprb_cmd_submit(iprb_t *ip, uint16_t cmd)
72882743679SGarrett D'Amore {
72982743679SGarrett D'Amore 	iprb_dma_t	*ncb = &ip->cmds[ip->cmd_head];
73082743679SGarrett D'Amore 	iprb_dma_t	*lcb = &ip->cmds[ip->cmd_last];
73182743679SGarrett D'Amore 
73282743679SGarrett D'Amore 	/* If this command will consume the last CB, interrupt when done */
73382743679SGarrett D'Amore 	ASSERT((ip->cmd_count) < NUM_TX);
73482743679SGarrett D'Amore 	if (ip->cmd_count == (NUM_TX - 1)) {
73582743679SGarrett D'Amore 		cmd |= CB_CMD_I;
73682743679SGarrett D'Amore 	}
73782743679SGarrett D'Amore 
73882743679SGarrett D'Amore 	/* clear the status entry */
73982743679SGarrett D'Amore 	PUTCB16(ncb, CB_STS_OFFSET, 0);
74082743679SGarrett D'Amore 
74182743679SGarrett D'Amore 	/* suspend upon completion of this new command */
74282743679SGarrett D'Amore 	cmd |= CB_CMD_S;
74382743679SGarrett D'Amore 	PUTCB16(ncb, CB_CMD_OFFSET, cmd);
74482743679SGarrett D'Amore 	SYNCCB(ncb, 0, 0, DDI_DMA_SYNC_FORDEV);
74582743679SGarrett D'Amore 
74682743679SGarrett D'Amore 	/* clear the suspend flag from the last submitted command */
74782743679SGarrett D'Amore 	SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
74882743679SGarrett D'Amore 	PUTCB16(lcb, CB_CMD_OFFSET, GETCB16(lcb, CB_CMD_OFFSET) & ~CB_CMD_S);
74982743679SGarrett D'Amore 	SYNCCB(lcb, CB_CMD_OFFSET, 2, DDI_DMA_SYNC_FORDEV);
75082743679SGarrett D'Amore 
75182743679SGarrett D'Amore 
75282743679SGarrett D'Amore 	/*
75382743679SGarrett D'Amore 	 * If the chip has a resume bug, then we need to try this as a work
75482743679SGarrett D'Amore 	 * around.  Some anecdotal evidence is that this will help solve
75582743679SGarrett D'Amore 	 * the resume bug.  Its a performance hit, but only if the EEPROM
75682743679SGarrett D'Amore 	 * is not updated.  (In theory we could do this only for 10Mbps HDX,
75782743679SGarrett D'Amore 	 * but since it should just about never get used, we keep it simple.)
75882743679SGarrett D'Amore 	 */
75982743679SGarrett D'Amore 	if (ip->resumebug) {
76082743679SGarrett D'Amore 		if (iprb_cmd_ready(ip) != DDI_SUCCESS)
76182743679SGarrett D'Amore 			return (DDI_FAILURE);
76282743679SGarrett D'Amore 		PUT8(ip, CSR_CMD, CUC_NOP);
76382743679SGarrett D'Amore 		(void) GET8(ip, CSR_CMD);
76482743679SGarrett D'Amore 		drv_usecwait(1);
76582743679SGarrett D'Amore 	}
76682743679SGarrett D'Amore 
76782743679SGarrett D'Amore 	/* wait for the SCB to be ready to accept a new command */
76882743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
76982743679SGarrett D'Amore 		return (DDI_FAILURE);
77082743679SGarrett D'Amore 
77182743679SGarrett D'Amore 	/*
77282743679SGarrett D'Amore 	 * Finally we can resume the CU.  Note that if this the first
77382743679SGarrett D'Amore 	 * command in the sequence (i.e. if the CU is IDLE), or if the
77482743679SGarrett D'Amore 	 * CU is already busy working, then this CU resume command
77582743679SGarrett D'Amore 	 * will not have any effect.
77682743679SGarrett D'Amore 	 */
77782743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, CUC_RESUME);
77882743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);	/* flush CSR */
77982743679SGarrett D'Amore 
7800529d5c6SJosef 'Jeff' Sipek 	ip->tx_wdog = gethrtime();
78182743679SGarrett D'Amore 	ip->cmd_last = ip->cmd_head;
78282743679SGarrett D'Amore 	ip->cmd_head++;
78382743679SGarrett D'Amore 	ip->cmd_head %= NUM_TX;
78482743679SGarrett D'Amore 	ip->cmd_count++;
78582743679SGarrett D'Amore 
78682743679SGarrett D'Amore 	return (DDI_SUCCESS);
78782743679SGarrett D'Amore }
78882743679SGarrett D'Amore 
78982743679SGarrett D'Amore iprb_dma_t *
iprb_cmd_next(iprb_t * ip)79082743679SGarrett D'Amore iprb_cmd_next(iprb_t *ip)
79182743679SGarrett D'Amore {
7929ca05893SAndy Fiddaman 	if (ip->cmd_count >= NUM_TX) {
79382743679SGarrett D'Amore 		return (NULL);
79482743679SGarrett D'Amore 	}
79582743679SGarrett D'Amore 	return (&ip->cmds[ip->cmd_head]);
79682743679SGarrett D'Amore }
79782743679SGarrett D'Amore 
79882743679SGarrett D'Amore int
iprb_set_unicast(iprb_t * ip)79982743679SGarrett D'Amore iprb_set_unicast(iprb_t *ip)
80082743679SGarrett D'Amore {
80182743679SGarrett D'Amore 	iprb_dma_t	*cb;
80282743679SGarrett D'Amore 
80382743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
80482743679SGarrett D'Amore 
80582743679SGarrett D'Amore 	if ((cb = iprb_cmd_next(ip)) == NULL)
80682743679SGarrett D'Amore 		return (DDI_FAILURE);
80782743679SGarrett D'Amore 
80882743679SGarrett D'Amore 	PUTCBEA(cb, CB_IAS_ADR_OFFSET, ip->curraddr);
80982743679SGarrett D'Amore 	return (iprb_cmd_submit(ip, CB_CMD_IAS));
81082743679SGarrett D'Amore }
81182743679SGarrett D'Amore 
81282743679SGarrett D'Amore int
iprb_set_multicast(iprb_t * ip)81382743679SGarrett D'Amore iprb_set_multicast(iprb_t *ip)
81482743679SGarrett D'Amore {
81582743679SGarrett D'Amore 	iprb_dma_t	*cb;
81682743679SGarrett D'Amore 	iprb_mcast_t	*mc;
81782743679SGarrett D'Amore 	int		i;
81882743679SGarrett D'Amore 	list_t		*l;
81982743679SGarrett D'Amore 
82082743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
82182743679SGarrett D'Amore 
82282743679SGarrett D'Amore 	if ((ip->nmcast <= 0) || (ip->nmcast > CB_MCS_CNT_MAX)) {
82382743679SGarrett D'Amore 		/*
82482743679SGarrett D'Amore 		 * Only send the list if the total number of multicast
82582743679SGarrett D'Amore 		 * address is nonzero and small enough to fit.  We
82682743679SGarrett D'Amore 		 * don't error out if it is too big, because in that
82782743679SGarrett D'Amore 		 * case we will use the "allmulticast" support
82882743679SGarrett D'Amore 		 * via iprb_set_config instead.
82982743679SGarrett D'Amore 		 */
83082743679SGarrett D'Amore 		return (DDI_SUCCESS);
83182743679SGarrett D'Amore 	}
83282743679SGarrett D'Amore 
83382743679SGarrett D'Amore 	if ((cb = iprb_cmd_next(ip)) == NULL) {
83482743679SGarrett D'Amore 		return (DDI_FAILURE);
83582743679SGarrett D'Amore 	}
83682743679SGarrett D'Amore 
83782743679SGarrett D'Amore 	l = &ip->mcast;
83882743679SGarrett D'Amore 	for (mc = list_head(l), i = 0; mc; mc = list_next(l, mc), i++) {
83982743679SGarrett D'Amore 		PUTCBEA(cb, CB_MCS_ADR_OFFSET + (i * 6), mc->addr);
84082743679SGarrett D'Amore 	}
84182743679SGarrett D'Amore 	ASSERT(i == ip->nmcast);
84282743679SGarrett D'Amore 	PUTCB16(cb, CB_MCS_CNT_OFFSET, i);
84382743679SGarrett D'Amore 	return (iprb_cmd_submit(ip, CB_CMD_MCS));
84482743679SGarrett D'Amore }
84582743679SGarrett D'Amore 
84682743679SGarrett D'Amore int
iprb_set_config(iprb_t * ip)84782743679SGarrett D'Amore iprb_set_config(iprb_t *ip)
84882743679SGarrett D'Amore {
84982743679SGarrett D'Amore 	iprb_dma_t *cb;
85082743679SGarrett D'Amore 
85182743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
85282743679SGarrett D'Amore 	if ((cb = iprb_cmd_next(ip)) == NULL) {
85382743679SGarrett D'Amore 		return (DDI_FAILURE);
85482743679SGarrett D'Amore 	}
85582743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 0, 0x16);
85682743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 1, 0x8);
85782743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 2, 0);
85882743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 3, (ip->canmwi ? 1 : 0));
85982743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 4, 0);
86082743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 5, 0);
86182743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 6, (ip->promisc ? 0x80 : 0) | 0x3a);
86282743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 7, (ip->promisc ? 0 : 0x1) | 2);
86382743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 8, (ip->miih ? 0x1 : 0));
86482743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 9, 0);
86582743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 10, 0x2e);
86682743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 11, 0);
86782743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 12, (ip->is557 ? 0 : 1) | 0x60);
86882743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 13, 0);
86982743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 14, 0xf2);
87082743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 15,
87182743679SGarrett D'Amore 	    (ip->miih ? 0x80 : 0) | (ip->promisc ? 0x1 : 0) | 0x48);
87282743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 16, 0);
87382743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 17, (ip->canpause ? 0x40 : 0));
87482743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 18, (ip->is557 ? 0 : 0x8) | 0xf2);
87582743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 19,
87682743679SGarrett D'Amore 	    ((ip->revid < REV_82558_B0) ? 0 : 0x80) |
87782743679SGarrett D'Amore 	    (ip->canpause ? 0x18 : 0));
87882743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 20, 0x3f);
87982743679SGarrett D'Amore 	PUTCB8(cb, CB_CONFIG_OFFSET + 21,
88082743679SGarrett D'Amore 	    ((ip->nmcast >= CB_MCS_CNT_MAX) ? 0x8 : 0) | 0x5);
88182743679SGarrett D'Amore 
88282743679SGarrett D'Amore 	return (iprb_cmd_submit(ip, CB_CMD_CONFIG));
88382743679SGarrett D'Amore }
88482743679SGarrett D'Amore 
88582743679SGarrett D'Amore int
iprb_set_ucode(iprb_t * ip)88682743679SGarrett D'Amore iprb_set_ucode(iprb_t *ip)
88782743679SGarrett D'Amore {
88882743679SGarrett D'Amore 	iprb_dma_t *cb;
88982743679SGarrett D'Amore 	const iprb_ucode_t *uc = NULL;
89082743679SGarrett D'Amore 	int i;
89182743679SGarrett D'Amore 
89282743679SGarrett D'Amore 	for (i = 0; iprb_ucode[i].length; i++) {
89382743679SGarrett D'Amore 		if (iprb_ucode[i].rev == ip->revid) {
89482743679SGarrett D'Amore 			uc = &iprb_ucode[i];
89582743679SGarrett D'Amore 			break;
89682743679SGarrett D'Amore 		}
89782743679SGarrett D'Amore 	}
89882743679SGarrett D'Amore 	if (uc == NULL) {
89982743679SGarrett D'Amore 		/* no matching firmware found, assume success */
90082743679SGarrett D'Amore 		return (DDI_SUCCESS);
90182743679SGarrett D'Amore 	}
90282743679SGarrett D'Amore 
90382743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
90482743679SGarrett D'Amore 	if ((cb = iprb_cmd_next(ip)) == NULL) {
90582743679SGarrett D'Amore 		return (DDI_FAILURE);
90682743679SGarrett D'Amore 	}
90782743679SGarrett D'Amore 	for (i = 0; i < uc->length; i++) {
90882743679SGarrett D'Amore 		PUTCB32(cb, (CB_UCODE_OFFSET + i * 4), uc->ucode[i]);
90982743679SGarrett D'Amore 	}
91082743679SGarrett D'Amore 	return (iprb_cmd_submit(ip, CB_CMD_UCODE));
91182743679SGarrett D'Amore }
91282743679SGarrett D'Amore 
91382743679SGarrett D'Amore int
iprb_configure(iprb_t * ip)91482743679SGarrett D'Amore iprb_configure(iprb_t *ip)
91582743679SGarrett D'Amore {
91682743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
91782743679SGarrett D'Amore 
91882743679SGarrett D'Amore 	if (iprb_cmd_drain(ip) != DDI_SUCCESS)
91982743679SGarrett D'Amore 		return (DDI_FAILURE);
92082743679SGarrett D'Amore 
92182743679SGarrett D'Amore 	if (iprb_set_config(ip) != DDI_SUCCESS)
92282743679SGarrett D'Amore 		return (DDI_FAILURE);
92382743679SGarrett D'Amore 	if (iprb_set_unicast(ip) != DDI_SUCCESS)
92482743679SGarrett D'Amore 		return (DDI_FAILURE);
92582743679SGarrett D'Amore 	if (iprb_set_multicast(ip) != DDI_SUCCESS)
92682743679SGarrett D'Amore 		return (DDI_FAILURE);
92782743679SGarrett D'Amore 
92882743679SGarrett D'Amore 	return (DDI_SUCCESS);
92982743679SGarrett D'Amore }
93082743679SGarrett D'Amore 
93182743679SGarrett D'Amore void
iprb_stop(iprb_t * ip)93282743679SGarrett D'Amore iprb_stop(iprb_t *ip)
93382743679SGarrett D'Amore {
93482743679SGarrett D'Amore 	/* go idle */
93582743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SEL_RESET);
93682743679SGarrett D'Amore 	(void) GET32(ip, CSR_PORT);
93782743679SGarrett D'Amore 	drv_usecwait(50);
93882743679SGarrett D'Amore 
93982743679SGarrett D'Amore 	/* shut off device interrupts */
94082743679SGarrett D'Amore 	PUT8(ip, CSR_INTCTL, INTCTL_MASK);
94182743679SGarrett D'Amore }
94282743679SGarrett D'Amore 
94382743679SGarrett D'Amore int
iprb_start(iprb_t * ip)94482743679SGarrett D'Amore iprb_start(iprb_t *ip)
94582743679SGarrett D'Amore {
94682743679SGarrett D'Amore 	iprb_dma_t *cb;
94782743679SGarrett D'Amore 
94882743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->rulock));
94982743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
95082743679SGarrett D'Amore 
95182743679SGarrett D'Amore 	/* Reset, but first go into idle state */
95282743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SEL_RESET);
95382743679SGarrett D'Amore 	(void) GET32(ip, CSR_PORT);
95482743679SGarrett D'Amore 	drv_usecwait(50);
95582743679SGarrett D'Amore 
95682743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SW_RESET);
95782743679SGarrett D'Amore 	(void) GET32(ip, CSR_PORT);
95882743679SGarrett D'Amore 	drv_usecwait(10);
95982743679SGarrett D'Amore 	PUT8(ip, CSR_INTCTL, INTCTL_MASK);
96082743679SGarrett D'Amore 
96182743679SGarrett D'Amore 	/* Reset pointers */
96282743679SGarrett D'Amore 	ip->cmd_head = ip->cmd_tail = 0;
96382743679SGarrett D'Amore 	ip->cmd_last = NUM_TX - 1;
9649ca05893SAndy Fiddaman 	ip->cmd_count = 0;
96582743679SGarrett D'Amore 
96682743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
96782743679SGarrett D'Amore 		return (DDI_FAILURE);
96882743679SGarrett D'Amore 	PUT32(ip, CSR_GEN_PTR, 0);
96982743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, CUC_CUBASE);
97082743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);
97182743679SGarrett D'Amore 
97282743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
97382743679SGarrett D'Amore 		return (DDI_FAILURE);
97482743679SGarrett D'Amore 	PUT32(ip, CSR_GEN_PTR, 0);
97582743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, RUC_RUBASE);
97682743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);
97782743679SGarrett D'Amore 
97882743679SGarrett D'Amore 	/* Send a NOP.  This will be the first command seen by the device. */
97982743679SGarrett D'Amore 	cb = iprb_cmd_next(ip);
9809ca05893SAndy Fiddaman 	VERIFY3P(cb, !=, NULL);
98182743679SGarrett D'Amore 	if (iprb_cmd_submit(ip, CB_CMD_NOP) != DDI_SUCCESS)
98282743679SGarrett D'Amore 		return (DDI_FAILURE);
98382743679SGarrett D'Amore 
98482743679SGarrett D'Amore 	/* as that was the first command, go ahead and submit a CU start */
98582743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
98682743679SGarrett D'Amore 		return (DDI_FAILURE);
98782743679SGarrett D'Amore 	PUT32(ip, CSR_GEN_PTR, cb->paddr);
98882743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, CUC_START);
98982743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);
99082743679SGarrett D'Amore 
99182743679SGarrett D'Amore 	/* Upload firmware. */
99282743679SGarrett D'Amore 	if (iprb_set_ucode(ip) != DDI_SUCCESS)
99382743679SGarrett D'Amore 		return (DDI_FAILURE);
99482743679SGarrett D'Amore 
99582743679SGarrett D'Amore 	/* Set up RFDs */
99682743679SGarrett D'Amore 	iprb_rx_init(ip);
99782743679SGarrett D'Amore 
99882743679SGarrett D'Amore 	PUT32(ip, CSR_GEN_PTR, ip->rxb[0].paddr);
99982743679SGarrett D'Amore 	/* wait for the SCB */
100082743679SGarrett D'Amore 	(void) iprb_cmd_ready(ip);
100182743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, RUC_START);
100282743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);	/* flush CSR */
100382743679SGarrett D'Amore 
100482743679SGarrett D'Amore 	/* Enable device interrupts */
100582743679SGarrett D'Amore 	PUT8(ip, CSR_INTCTL, 0);
100682743679SGarrett D'Amore 	(void) GET8(ip, CSR_INTCTL);
100782743679SGarrett D'Amore 
100882743679SGarrett D'Amore 	return (DDI_SUCCESS);
100982743679SGarrett D'Amore }
101082743679SGarrett D'Amore 
101182743679SGarrett D'Amore void
iprb_update_stats(iprb_t * ip)101282743679SGarrett D'Amore iprb_update_stats(iprb_t *ip)
101382743679SGarrett D'Amore {
101482743679SGarrett D'Amore 	iprb_dma_t	*sp = &ip->stats;
10150529d5c6SJosef 'Jeff' Sipek 	hrtime_t	tstamp;
101682743679SGarrett D'Amore 	int		i;
101782743679SGarrett D'Amore 
101882743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
101982743679SGarrett D'Amore 
102082743679SGarrett D'Amore 	/* Collect the hardware stats, but don't keep redoing it */
10210529d5c6SJosef 'Jeff' Sipek 	tstamp = gethrtime();
10220529d5c6SJosef 'Jeff' Sipek 	if (tstamp / NANOSEC == ip->stats_time / NANOSEC)
102382743679SGarrett D'Amore 		return;
102482743679SGarrett D'Amore 
102582743679SGarrett D'Amore 	PUTSTAT(sp, STATS_DONE_OFFSET, 0);
102682743679SGarrett D'Amore 	SYNCSTATS(sp, 0, 0, DDI_DMA_SYNC_FORDEV);
102782743679SGarrett D'Amore 
102882743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
102982743679SGarrett D'Amore 		return;
103082743679SGarrett D'Amore 	PUT32(ip, CSR_GEN_PTR, sp->paddr);
103182743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, CUC_STATSBASE);
103282743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);
103382743679SGarrett D'Amore 
103482743679SGarrett D'Amore 	if (iprb_cmd_ready(ip) != DDI_SUCCESS)
103582743679SGarrett D'Amore 		return;
103682743679SGarrett D'Amore 	PUT8(ip, CSR_CMD, CUC_STATS_RST);
103782743679SGarrett D'Amore 	(void) GET8(ip, CSR_CMD);	/* flush wb */
103882743679SGarrett D'Amore 
103982743679SGarrett D'Amore 	for (i = 10000; i; i -= 10) {
104082743679SGarrett D'Amore 		SYNCSTATS(sp, 0, 0, DDI_DMA_SYNC_FORKERNEL);
104182743679SGarrett D'Amore 		if (GETSTAT(sp, STATS_DONE_OFFSET) == STATS_RST_DONE) {
104282743679SGarrett D'Amore 			/* yay stats are updated */
104382743679SGarrett D'Amore 			break;
104482743679SGarrett D'Amore 		}
104582743679SGarrett D'Amore 		drv_usecwait(10);
104682743679SGarrett D'Amore 	}
104782743679SGarrett D'Amore 	if (i == 0) {
104882743679SGarrett D'Amore 		iprb_error(ip, "time out acquiring hardware statistics");
104982743679SGarrett D'Amore 		return;
105082743679SGarrett D'Amore 	}
105182743679SGarrett D'Amore 
105282743679SGarrett D'Amore 	ip->ex_coll += GETSTAT(sp, STATS_TX_MAXCOL_OFFSET);
105382743679SGarrett D'Amore 	ip->late_coll += GETSTAT(sp, STATS_TX_LATECOL_OFFSET);
105482743679SGarrett D'Amore 	ip->uflo += GETSTAT(sp, STATS_TX_UFLO_OFFSET);
105582743679SGarrett D'Amore 	ip->defer_xmt += GETSTAT(sp, STATS_TX_DEFER_OFFSET);
105682743679SGarrett D'Amore 	ip->one_coll += GETSTAT(sp, STATS_TX_ONECOL_OFFSET);
105782743679SGarrett D'Amore 	ip->multi_coll += GETSTAT(sp, STATS_TX_MULTCOL_OFFSET);
105882743679SGarrett D'Amore 	ip->collisions += GETSTAT(sp, STATS_TX_TOTCOL_OFFSET);
105982743679SGarrett D'Amore 	ip->fcs_errs += GETSTAT(sp, STATS_RX_FCS_OFFSET);
106082743679SGarrett D'Amore 	ip->align_errs += GETSTAT(sp, STATS_RX_ALIGN_OFFSET);
106182743679SGarrett D'Amore 	ip->norcvbuf += GETSTAT(sp, STATS_RX_NOBUF_OFFSET);
106282743679SGarrett D'Amore 	ip->oflo += GETSTAT(sp, STATS_RX_OFLO_OFFSET);
106382743679SGarrett D'Amore 	ip->runt += GETSTAT(sp, STATS_RX_SHORT_OFFSET);
106482743679SGarrett D'Amore 
106582743679SGarrett D'Amore 	ip->stats_time = tstamp;
106682743679SGarrett D'Amore }
106782743679SGarrett D'Amore 
106882743679SGarrett D'Amore mblk_t *
iprb_send(iprb_t * ip,mblk_t * mp)106982743679SGarrett D'Amore iprb_send(iprb_t *ip, mblk_t *mp)
107082743679SGarrett D'Amore {
107182743679SGarrett D'Amore 	iprb_dma_t	*cb;
107282743679SGarrett D'Amore 	size_t		sz;
107382743679SGarrett D'Amore 
107482743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->culock));
107582743679SGarrett D'Amore 
107682743679SGarrett D'Amore 	/* possibly reclaim some CBs */
107782743679SGarrett D'Amore 	iprb_cmd_reclaim(ip);
107882743679SGarrett D'Amore 
107982743679SGarrett D'Amore 	cb = iprb_cmd_next(ip);
108082743679SGarrett D'Amore 
108182743679SGarrett D'Amore 	if (cb == NULL) {
108282743679SGarrett D'Amore 		/* flow control */
108382743679SGarrett D'Amore 		ip->wantw = B_TRUE;
108482743679SGarrett D'Amore 		return (mp);
108582743679SGarrett D'Amore 	}
108682743679SGarrett D'Amore 
108782743679SGarrett D'Amore 	if ((sz = msgsize(mp)) > (ETHERMAX + VLAN_TAGSZ)) {
108882743679SGarrett D'Amore 		/* Generally this should never occur */
108982743679SGarrett D'Amore 		ip->macxmt_errs++;
109082743679SGarrett D'Amore 		freemsg(mp);
109182743679SGarrett D'Amore 		return (NULL);
109282743679SGarrett D'Amore 	}
109382743679SGarrett D'Amore 
109482743679SGarrett D'Amore 	ip->opackets++;
109582743679SGarrett D'Amore 	ip->obytes += sz;
109682743679SGarrett D'Amore 
109782743679SGarrett D'Amore 	PUTCB32(cb, CB_TX_TBD_OFFSET, 0xffffffffU);
109882743679SGarrett D'Amore 	PUTCB16(cb, CB_TX_COUNT_OFFSET, (sz & 0x3fff) | CB_TX_EOF);
109982743679SGarrett D'Amore 	PUTCB8(cb, CB_TX_THRESH_OFFSET, (sz / 8) & 0xff);
110082743679SGarrett D'Amore 	PUTCB8(cb, CB_TX_NUMBER_OFFSET, 0);
110182743679SGarrett D'Amore 	mcopymsg(mp, cb->vaddr + CB_TX_DATA_OFFSET);
110282743679SGarrett D'Amore 	if (cb->vaddr[CB_TX_DATA_OFFSET] & 0x1) {
110382743679SGarrett D'Amore 		if (bcmp(cb->vaddr + CB_TX_DATA_OFFSET, &iprb_bcast, 6) != 0) {
110482743679SGarrett D'Amore 			ip->multixmt++;
110582743679SGarrett D'Amore 		} else {
110682743679SGarrett D'Amore 			ip->brdcstxmt++;
110782743679SGarrett D'Amore 		}
110882743679SGarrett D'Amore 	}
110982743679SGarrett D'Amore 	SYNCCB(cb, 0, CB_TX_DATA_OFFSET + sz, DDI_DMA_SYNC_FORDEV);
111082743679SGarrett D'Amore 
111182743679SGarrett D'Amore 	if (iprb_cmd_submit(ip, CB_CMD_TX) != DDI_SUCCESS) {
111282743679SGarrett D'Amore 		ip->macxmt_errs++;
111382743679SGarrett D'Amore 	}
111482743679SGarrett D'Amore 
111582743679SGarrett D'Amore 	return (NULL);
111682743679SGarrett D'Amore }
111782743679SGarrett D'Amore 
111882743679SGarrett D'Amore void
iprb_rx_add(iprb_t * ip)111982743679SGarrett D'Amore iprb_rx_add(iprb_t *ip)
112082743679SGarrett D'Amore {
112182743679SGarrett D'Amore 	uint16_t	last, curr, next;
112282743679SGarrett D'Amore 	iprb_dma_t	*rfd, *nfd, *lfd;
112382743679SGarrett D'Amore 
112482743679SGarrett D'Amore 	ASSERT(mutex_owned(&ip->rulock));
112582743679SGarrett D'Amore 
112682743679SGarrett D'Amore 	curr = ip->rx_index;
112782743679SGarrett D'Amore 	last = ip->rx_last;
112882743679SGarrett D'Amore 	next = (curr + 1) % NUM_RX;
112982743679SGarrett D'Amore 
113082743679SGarrett D'Amore 	ip->rx_last = curr;
113182743679SGarrett D'Amore 	ip->rx_index = next;
113282743679SGarrett D'Amore 
113382743679SGarrett D'Amore 	lfd = &ip->rxb[last];
113482743679SGarrett D'Amore 	rfd = &ip->rxb[curr];
113582743679SGarrett D'Amore 	nfd = &ip->rxb[next];
113682743679SGarrett D'Amore 
113782743679SGarrett D'Amore 	PUTRFD32(rfd, RFD_LNK_OFFSET, nfd->paddr);
113882743679SGarrett D'Amore 	PUTRFD16(rfd, RFD_CTL_OFFSET, RFD_CTL_EL);
113982743679SGarrett D'Amore 	PUTRFD16(rfd, RFD_SIZ_OFFSET, RFD_SIZE - RFD_PKT_OFFSET);
114082743679SGarrett D'Amore 	PUTRFD16(rfd, RFD_CNT_OFFSET, 0);
114182743679SGarrett D'Amore 	SYNCRFD(rfd, 0, RFD_PKT_OFFSET, DDI_DMA_SYNC_FORDEV);
114282743679SGarrett D'Amore 	/* clear the suspend & EL bits from the previous RFD */
114382743679SGarrett D'Amore 	PUTRFD16(lfd, RFD_CTL_OFFSET, 0);
114482743679SGarrett D'Amore 	SYNCRFD(rfd, RFD_CTL_OFFSET, 2, DDI_DMA_SYNC_FORDEV);
114582743679SGarrett D'Amore }
114682743679SGarrett D'Amore 
114782743679SGarrett D'Amore void
iprb_rx_init(iprb_t * ip)114882743679SGarrett D'Amore iprb_rx_init(iprb_t *ip)
114982743679SGarrett D'Amore {
115082743679SGarrett D'Amore 	ip->rx_index = 0;
115182743679SGarrett D'Amore 	ip->rx_last = NUM_RX - 1;
115282743679SGarrett D'Amore 	for (int i = 0; i < NUM_RX; i++)
115382743679SGarrett D'Amore 		iprb_rx_add(ip);
115482743679SGarrett D'Amore 	ip->rx_index = 0;
115582743679SGarrett D'Amore 	ip->rx_last = NUM_RX - 1;
115682743679SGarrett D'Amore }
115782743679SGarrett D'Amore 
115882743679SGarrett D'Amore mblk_t *
iprb_rx(iprb_t * ip)115982743679SGarrett D'Amore iprb_rx(iprb_t *ip)
116082743679SGarrett D'Amore {
116182743679SGarrett D'Amore 	iprb_dma_t	*rfd;
116282743679SGarrett D'Amore 	uint16_t	cnt;
116382743679SGarrett D'Amore 	uint16_t	sts;
116482743679SGarrett D'Amore 	int		i;
116582743679SGarrett D'Amore 	mblk_t		*mplist;
116682743679SGarrett D'Amore 	mblk_t		**mpp;
116782743679SGarrett D'Amore 	mblk_t		*mp;
116882743679SGarrett D'Amore 
116982743679SGarrett D'Amore 	mplist = NULL;
117082743679SGarrett D'Amore 	mpp = &mplist;
117182743679SGarrett D'Amore 
117282743679SGarrett D'Amore 	for (i = 0; i < NUM_RX; i++) {
117382743679SGarrett D'Amore 		rfd = &ip->rxb[ip->rx_index];
117482743679SGarrett D'Amore 		SYNCRFD(rfd, RFD_STS_OFFSET, 2, DDI_DMA_SYNC_FORKERNEL);
117582743679SGarrett D'Amore 		if ((GETRFD16(rfd, RFD_STS_OFFSET) & RFD_STS_C) == 0) {
117682743679SGarrett D'Amore 			break;
117782743679SGarrett D'Amore 		}
117882743679SGarrett D'Amore 
11790529d5c6SJosef 'Jeff' Sipek 		ip->rx_wdog = gethrtime();
118082743679SGarrett D'Amore 
118182743679SGarrett D'Amore 		SYNCRFD(rfd, 0, 0, DDI_DMA_SYNC_FORKERNEL);
118282743679SGarrett D'Amore 		cnt = GETRFD16(rfd, RFD_CNT_OFFSET);
118382743679SGarrett D'Amore 		cnt &= ~(RFD_CNT_EOF | RFD_CNT_F);
118482743679SGarrett D'Amore 		sts = GETRFD16(rfd, RFD_STS_OFFSET);
118582743679SGarrett D'Amore 
118682743679SGarrett D'Amore 		if (cnt > (ETHERMAX + VLAN_TAGSZ)) {
118782743679SGarrett D'Amore 			ip->toolong++;
118882743679SGarrett D'Amore 			iprb_rx_add(ip);
118982743679SGarrett D'Amore 			continue;
119082743679SGarrett D'Amore 		}
119182743679SGarrett D'Amore 		if (((sts & RFD_STS_OK) == 0) && (sts & RFD_STS_ERRS)) {
119282743679SGarrett D'Amore 			iprb_rx_add(ip);
119382743679SGarrett D'Amore 			continue;
119482743679SGarrett D'Amore 		}
119582743679SGarrett D'Amore 		if ((mp = allocb(cnt, BPRI_MED)) == NULL) {
119682743679SGarrett D'Amore 			ip->norcvbuf++;
119782743679SGarrett D'Amore 			iprb_rx_add(ip);
119882743679SGarrett D'Amore 			continue;
119982743679SGarrett D'Amore 		}
120082743679SGarrett D'Amore 		bcopy(rfd->vaddr + RFD_PKT_OFFSET, mp->b_wptr, cnt);
120182743679SGarrett D'Amore 
120282743679SGarrett D'Amore 		/* return it to the RFD list */
120382743679SGarrett D'Amore 		iprb_rx_add(ip);
120482743679SGarrett D'Amore 
120582743679SGarrett D'Amore 		mp->b_wptr += cnt;
120682743679SGarrett D'Amore 		ip->ipackets++;
120782743679SGarrett D'Amore 		ip->rbytes += cnt;
120882743679SGarrett D'Amore 		if (mp->b_rptr[0] & 0x1) {
120982743679SGarrett D'Amore 			if (bcmp(mp->b_rptr, &iprb_bcast, 6) != 0) {
121082743679SGarrett D'Amore 				ip->multircv++;
121182743679SGarrett D'Amore 			} else {
121282743679SGarrett D'Amore 				ip->brdcstrcv++;
121382743679SGarrett D'Amore 			}
121482743679SGarrett D'Amore 		}
121582743679SGarrett D'Amore 		*mpp = mp;
121682743679SGarrett D'Amore 		mpp = &mp->b_next;
121782743679SGarrett D'Amore 	}
121882743679SGarrett D'Amore 	return (mplist);
121982743679SGarrett D'Amore }
122082743679SGarrett D'Amore 
122182743679SGarrett D'Amore int
iprb_m_promisc(void * arg,boolean_t on)122282743679SGarrett D'Amore iprb_m_promisc(void *arg, boolean_t on)
122382743679SGarrett D'Amore {
122482743679SGarrett D'Amore 	iprb_t *ip = arg;
122582743679SGarrett D'Amore 
122682743679SGarrett D'Amore 	mutex_enter(&ip->culock);
122782743679SGarrett D'Amore 	ip->promisc = on;
122882743679SGarrett D'Amore 	if (ip->running && !ip->suspended)
122982743679SGarrett D'Amore 		(void) iprb_configure(ip);
123082743679SGarrett D'Amore 	mutex_exit(&ip->culock);
123182743679SGarrett D'Amore 	return (0);
123282743679SGarrett D'Amore }
123382743679SGarrett D'Amore 
123482743679SGarrett D'Amore int
iprb_m_unicst(void * arg,const uint8_t * macaddr)123582743679SGarrett D'Amore iprb_m_unicst(void *arg, const uint8_t *macaddr)
123682743679SGarrett D'Amore {
123782743679SGarrett D'Amore 	iprb_t *ip = arg;
123882743679SGarrett D'Amore 
123982743679SGarrett D'Amore 	mutex_enter(&ip->culock);
124082743679SGarrett D'Amore 	bcopy(macaddr, ip->curraddr, 6);
124182743679SGarrett D'Amore 	if (ip->running && !ip->suspended)
124282743679SGarrett D'Amore 		(void) iprb_configure(ip);
124382743679SGarrett D'Amore 	mutex_exit(&ip->culock);
124482743679SGarrett D'Amore 	return (0);
124582743679SGarrett D'Amore }
124682743679SGarrett D'Amore 
124782743679SGarrett D'Amore int
iprb_m_multicst(void * arg,boolean_t add,const uint8_t * macaddr)124882743679SGarrett D'Amore iprb_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
124982743679SGarrett D'Amore {
125082743679SGarrett D'Amore 	iprb_t		*ip = arg;
125182743679SGarrett D'Amore 	list_t		*l = &ip->mcast;
125282743679SGarrett D'Amore 	iprb_mcast_t	*mc;
125382743679SGarrett D'Amore 
125482743679SGarrett D'Amore 	if (add) {
125582743679SGarrett D'Amore 		mc = kmem_alloc(sizeof (*mc), KM_NOSLEEP);
125682743679SGarrett D'Amore 		if (mc == NULL) {
125782743679SGarrett D'Amore 			return (ENOMEM);
125882743679SGarrett D'Amore 		}
125982743679SGarrett D'Amore 		bcopy(macaddr, mc->addr, 6);
126082743679SGarrett D'Amore 		mutex_enter(&ip->culock);
126182743679SGarrett D'Amore 		list_insert_head(l, mc);
126282743679SGarrett D'Amore 		ip->nmcast++;
126382743679SGarrett D'Amore 		if (ip->running && !ip->suspended)
126482743679SGarrett D'Amore 			(void) iprb_configure(ip);
126582743679SGarrett D'Amore 		mutex_exit(&ip->culock);
126682743679SGarrett D'Amore 	} else {
126782743679SGarrett D'Amore 		mutex_enter(&ip->culock);
126882743679SGarrett D'Amore 		for (mc = list_head(l); mc != NULL; mc = list_next(l, mc)) {
126982743679SGarrett D'Amore 			if (bcmp(macaddr, mc->addr, 6) == 0) {
127082743679SGarrett D'Amore 				list_remove(&ip->mcast, mc);
127182743679SGarrett D'Amore 				ip->nmcast--;
127282743679SGarrett D'Amore 				if (ip->running && !ip->suspended)
127382743679SGarrett D'Amore 					(void) iprb_configure(ip);
127482743679SGarrett D'Amore 				break;
127582743679SGarrett D'Amore 			}
127682743679SGarrett D'Amore 		}
127782743679SGarrett D'Amore 		mutex_exit(&ip->culock);
127882743679SGarrett D'Amore 		if (mc)
127982743679SGarrett D'Amore 			kmem_free(mc, sizeof (*mc));
128082743679SGarrett D'Amore 	}
128182743679SGarrett D'Amore 	return (0);
128282743679SGarrett D'Amore }
128382743679SGarrett D'Amore 
128482743679SGarrett D'Amore int
iprb_m_start(void * arg)128582743679SGarrett D'Amore iprb_m_start(void *arg)
128682743679SGarrett D'Amore {
128782743679SGarrett D'Amore 	int rv;
128882743679SGarrett D'Amore 	iprb_t *ip = arg;
128982743679SGarrett D'Amore 
129082743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
129182743679SGarrett D'Amore 	mutex_enter(&ip->culock);
129282743679SGarrett D'Amore 	rv = ip->suspended ? 0 : iprb_start(ip);
129382743679SGarrett D'Amore 	if (rv == 0)
129482743679SGarrett D'Amore 		ip->running = B_TRUE;
129582743679SGarrett D'Amore 	ip->perh = ddi_periodic_add(iprb_periodic, ip, 5000000000, 0);
129682743679SGarrett D'Amore 	mutex_exit(&ip->culock);
129782743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
129882743679SGarrett D'Amore 	if (rv == 0) {
129982743679SGarrett D'Amore 		if (ip->miih)
130082743679SGarrett D'Amore 			mii_start(ip->miih);
130182743679SGarrett D'Amore 		else
130282743679SGarrett D'Amore 			/* might be a lie. */
130382743679SGarrett D'Amore 			mac_link_update(ip->mach, LINK_STATE_UP);
130482743679SGarrett D'Amore 	}
130582743679SGarrett D'Amore 	return (rv ? EIO : 0);
130682743679SGarrett D'Amore }
130782743679SGarrett D'Amore 
130882743679SGarrett D'Amore void
iprb_m_stop(void * arg)130982743679SGarrett D'Amore iprb_m_stop(void *arg)
131082743679SGarrett D'Amore {
131182743679SGarrett D'Amore 	iprb_t *ip = arg;
131282743679SGarrett D'Amore 
131382743679SGarrett D'Amore 	if (ip->miih) {
131482743679SGarrett D'Amore 		mii_stop(ip->miih);
131582743679SGarrett D'Amore 	} else {
131682743679SGarrett D'Amore 		mac_link_update(ip->mach, LINK_STATE_DOWN);
131782743679SGarrett D'Amore 	}
1318197c9523SMarcel Telka 
1319197c9523SMarcel Telka 	ddi_periodic_delete(ip->perh);
1320197c9523SMarcel Telka 	ip->perh = 0;
1321197c9523SMarcel Telka 
132282743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
132382743679SGarrett D'Amore 	mutex_enter(&ip->culock);
132482743679SGarrett D'Amore 
132582743679SGarrett D'Amore 	if (!ip->suspended) {
132682743679SGarrett D'Amore 		iprb_update_stats(ip);
132782743679SGarrett D'Amore 		iprb_stop(ip);
132882743679SGarrett D'Amore 	}
132982743679SGarrett D'Amore 	ip->running = B_FALSE;
133082743679SGarrett D'Amore 	mutex_exit(&ip->culock);
133182743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
133282743679SGarrett D'Amore }
133382743679SGarrett D'Amore 
133482743679SGarrett D'Amore int
iprb_m_stat(void * arg,uint_t stat,uint64_t * val)133582743679SGarrett D'Amore iprb_m_stat(void *arg, uint_t stat, uint64_t *val)
133682743679SGarrett D'Amore {
133782743679SGarrett D'Amore 	iprb_t		*ip = arg;
133882743679SGarrett D'Amore 
133982743679SGarrett D'Amore 	if (ip->miih && (mii_m_getstat(ip->miih, stat, val) == 0)) {
134082743679SGarrett D'Amore 		return (0);
134182743679SGarrett D'Amore 	}
134282743679SGarrett D'Amore 
134382743679SGarrett D'Amore 	mutex_enter(&ip->culock);
134482743679SGarrett D'Amore 	if ((!ip->suspended) && (ip->running)) {
134582743679SGarrett D'Amore 		iprb_update_stats(ip);
134682743679SGarrett D'Amore 	}
134782743679SGarrett D'Amore 	mutex_exit(&ip->culock);
134882743679SGarrett D'Amore 
134982743679SGarrett D'Amore 	switch (stat) {
135082743679SGarrett D'Amore 	case MAC_STAT_IFSPEED:
135182743679SGarrett D'Amore 		if (ip->miih == NULL) {
135282743679SGarrett D'Amore 			*val = 10000000;	/* 10 Mbps */
135382743679SGarrett D'Amore 		}
135482743679SGarrett D'Amore 		break;
135582743679SGarrett D'Amore 	case ETHER_STAT_LINK_DUPLEX:
135682743679SGarrett D'Amore 		if (ip->miih == NULL) {
135782743679SGarrett D'Amore 			*val = LINK_DUPLEX_UNKNOWN;
135882743679SGarrett D'Amore 		}
135982743679SGarrett D'Amore 		break;
136082743679SGarrett D'Amore 	case MAC_STAT_MULTIRCV:
136182743679SGarrett D'Amore 		*val = ip->multircv;
136282743679SGarrett D'Amore 		break;
136382743679SGarrett D'Amore 	case MAC_STAT_BRDCSTRCV:
136482743679SGarrett D'Amore 		*val = ip->brdcstrcv;
136582743679SGarrett D'Amore 		break;
136682743679SGarrett D'Amore 	case MAC_STAT_MULTIXMT:
136782743679SGarrett D'Amore 		*val = ip->multixmt;
136882743679SGarrett D'Amore 		break;
136982743679SGarrett D'Amore 	case MAC_STAT_BRDCSTXMT:
137082743679SGarrett D'Amore 		*val = ip->brdcstxmt;
137182743679SGarrett D'Amore 		break;
137282743679SGarrett D'Amore 	case MAC_STAT_IPACKETS:
137382743679SGarrett D'Amore 		* val = ip->ipackets;
137482743679SGarrett D'Amore 		break;
137582743679SGarrett D'Amore 	case MAC_STAT_RBYTES:
137682743679SGarrett D'Amore 		*val = ip->rbytes;
137782743679SGarrett D'Amore 		break;
137882743679SGarrett D'Amore 	case MAC_STAT_OPACKETS:
137982743679SGarrett D'Amore 		*val = ip->opackets;
138082743679SGarrett D'Amore 		break;
138182743679SGarrett D'Amore 	case MAC_STAT_OBYTES:
138282743679SGarrett D'Amore 		*val = ip->obytes;
138382743679SGarrett D'Amore 		break;
138482743679SGarrett D'Amore 	case MAC_STAT_NORCVBUF:
138582743679SGarrett D'Amore 		*val = ip->norcvbuf;
138682743679SGarrett D'Amore 		break;
138782743679SGarrett D'Amore 	case MAC_STAT_COLLISIONS:
138882743679SGarrett D'Amore 		*val = ip->collisions;
138982743679SGarrett D'Amore 		break;
139082743679SGarrett D'Amore 	case MAC_STAT_IERRORS:
139182743679SGarrett D'Amore 		*val = ip->align_errs +
139282743679SGarrett D'Amore 		    ip->fcs_errs +
139382743679SGarrett D'Amore 		    ip->norcvbuf +
139482743679SGarrett D'Amore 		    ip->runt +
139582743679SGarrett D'Amore 		    ip->toolong +
139682743679SGarrett D'Amore 		    ip->macrcv_errs;
139782743679SGarrett D'Amore 		break;
139882743679SGarrett D'Amore 	case MAC_STAT_OERRORS:
139982743679SGarrett D'Amore 		*val = ip->ex_coll +
140082743679SGarrett D'Amore 		    ip->late_coll +
140182743679SGarrett D'Amore 		    ip->uflo +
140282743679SGarrett D'Amore 		    ip->macxmt_errs +
140382743679SGarrett D'Amore 		    ip->nocarrier;
140482743679SGarrett D'Amore 		break;
140582743679SGarrett D'Amore 	case ETHER_STAT_ALIGN_ERRORS:
140682743679SGarrett D'Amore 		*val = ip->align_errs;
140782743679SGarrett D'Amore 		break;
140882743679SGarrett D'Amore 	case ETHER_STAT_FCS_ERRORS:
140982743679SGarrett D'Amore 		*val = ip->fcs_errs;
141082743679SGarrett D'Amore 		break;
141182743679SGarrett D'Amore 	case ETHER_STAT_DEFER_XMTS:
141282743679SGarrett D'Amore 		*val = ip->defer_xmt;
141382743679SGarrett D'Amore 		break;
141482743679SGarrett D'Amore 	case ETHER_STAT_FIRST_COLLISIONS:
141582743679SGarrett D'Amore 		*val = ip->one_coll + ip->multi_coll + ip->ex_coll;
141682743679SGarrett D'Amore 		break;
141782743679SGarrett D'Amore 	case ETHER_STAT_MULTI_COLLISIONS:
141882743679SGarrett D'Amore 		*val = ip->multi_coll;
141982743679SGarrett D'Amore 		break;
142082743679SGarrett D'Amore 	case ETHER_STAT_TX_LATE_COLLISIONS:
142182743679SGarrett D'Amore 		*val = ip->late_coll;
142282743679SGarrett D'Amore 		break;
142382743679SGarrett D'Amore 	case ETHER_STAT_EX_COLLISIONS:
142482743679SGarrett D'Amore 		*val = ip->ex_coll;
142582743679SGarrett D'Amore 		break;
142682743679SGarrett D'Amore 	case MAC_STAT_OVERFLOWS:
142782743679SGarrett D'Amore 		*val = ip->oflo;
142882743679SGarrett D'Amore 		break;
142982743679SGarrett D'Amore 	case MAC_STAT_UNDERFLOWS:
143082743679SGarrett D'Amore 		*val = ip->uflo;
143182743679SGarrett D'Amore 		break;
143282743679SGarrett D'Amore 	case ETHER_STAT_TOOSHORT_ERRORS:
143382743679SGarrett D'Amore 		*val = ip->runt;
143482743679SGarrett D'Amore 		break;
143582743679SGarrett D'Amore 	case ETHER_STAT_TOOLONG_ERRORS:
143682743679SGarrett D'Amore 		*val = ip->toolong;
143782743679SGarrett D'Amore 		break;
143882743679SGarrett D'Amore 	case ETHER_STAT_CARRIER_ERRORS:
143982743679SGarrett D'Amore 		*val = ip->nocarrier;	/* reported only for "suspend" */
144082743679SGarrett D'Amore 		break;
144182743679SGarrett D'Amore 	case ETHER_STAT_MACXMT_ERRORS:
144282743679SGarrett D'Amore 		*val = ip->macxmt_errs;
144382743679SGarrett D'Amore 		break;
144482743679SGarrett D'Amore 	case ETHER_STAT_MACRCV_ERRORS:
144582743679SGarrett D'Amore 		*val = ip->macrcv_errs;
144682743679SGarrett D'Amore 		break;
144782743679SGarrett D'Amore 	default:
144882743679SGarrett D'Amore 		return (ENOTSUP);
144982743679SGarrett D'Amore 	}
145082743679SGarrett D'Amore 	return (0);
145182743679SGarrett D'Amore }
145282743679SGarrett D'Amore 
145382743679SGarrett D'Amore void
iprb_m_propinfo(void * arg,const char * name,mac_prop_id_t id,mac_prop_info_handle_t pih)145482743679SGarrett D'Amore iprb_m_propinfo(void *arg, const char *name, mac_prop_id_t id,
145582743679SGarrett D'Amore     mac_prop_info_handle_t pih)
145682743679SGarrett D'Amore {
145782743679SGarrett D'Amore 	iprb_t *ip = arg;
145882743679SGarrett D'Amore 
145982743679SGarrett D'Amore 	if (ip->miih != NULL) {
146082743679SGarrett D'Amore 		mii_m_propinfo(ip->miih, name, id, pih);
146182743679SGarrett D'Amore 		return;
146282743679SGarrett D'Amore 	}
146382743679SGarrett D'Amore 	switch (id) {
146482743679SGarrett D'Amore 	case MAC_PROP_DUPLEX:
146582743679SGarrett D'Amore 	case MAC_PROP_SPEED:
146682743679SGarrett D'Amore 		mac_prop_info_set_perm(pih, MAC_PROP_PERM_READ);
146782743679SGarrett D'Amore 		break;
146882743679SGarrett D'Amore 	}
146982743679SGarrett D'Amore }
147082743679SGarrett D'Amore 
147182743679SGarrett D'Amore int
iprb_m_getprop(void * arg,const char * name,mac_prop_id_t id,uint_t sz,void * val)147282743679SGarrett D'Amore iprb_m_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz,
147382743679SGarrett D'Amore     void *val)
147482743679SGarrett D'Amore {
147582743679SGarrett D'Amore 	iprb_t *ip = arg;
147682743679SGarrett D'Amore 	uint64_t x;
147782743679SGarrett D'Amore 
147882743679SGarrett D'Amore 	if (ip->miih != NULL) {
147982743679SGarrett D'Amore 		return (mii_m_getprop(ip->miih, name, id, sz, val));
148082743679SGarrett D'Amore 	}
148182743679SGarrett D'Amore 	switch (id) {
148282743679SGarrett D'Amore 	case MAC_PROP_SPEED:
148382743679SGarrett D'Amore 		x = 10000000;
148482743679SGarrett D'Amore 		bcopy(&x, val, sizeof (x));
148582743679SGarrett D'Amore 		return (0);
148682743679SGarrett D'Amore 
148782743679SGarrett D'Amore 	case MAC_PROP_DUPLEX:
148882743679SGarrett D'Amore 		x = LINK_DUPLEX_UNKNOWN;
148982743679SGarrett D'Amore 		bcopy(&x, val, sizeof (x));
149082743679SGarrett D'Amore 		return (0);
149182743679SGarrett D'Amore 	}
149282743679SGarrett D'Amore 
149382743679SGarrett D'Amore 	return (ENOTSUP);
149482743679SGarrett D'Amore }
149582743679SGarrett D'Amore 
149682743679SGarrett D'Amore int
iprb_m_setprop(void * arg,const char * name,mac_prop_id_t id,uint_t sz,const void * val)149782743679SGarrett D'Amore iprb_m_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t sz,
149882743679SGarrett D'Amore     const void *val)
149982743679SGarrett D'Amore {
150082743679SGarrett D'Amore 	iprb_t *ip = arg;
150182743679SGarrett D'Amore 
150282743679SGarrett D'Amore 	if (ip->miih != NULL) {
150382743679SGarrett D'Amore 		return (mii_m_setprop(ip->miih, name, id, sz, val));
150482743679SGarrett D'Amore 	}
150582743679SGarrett D'Amore 	return (ENOTSUP);
150682743679SGarrett D'Amore }
150782743679SGarrett D'Amore 
150882743679SGarrett D'Amore mblk_t *
iprb_m_tx(void * arg,mblk_t * mp)150982743679SGarrett D'Amore iprb_m_tx(void *arg, mblk_t *mp)
151082743679SGarrett D'Amore {
151182743679SGarrett D'Amore 	iprb_t *ip = arg;
151282743679SGarrett D'Amore 	mblk_t *nmp;
151382743679SGarrett D'Amore 
151482743679SGarrett D'Amore 	mutex_enter(&ip->culock);
151582743679SGarrett D'Amore 
151682743679SGarrett D'Amore 	while (mp != NULL) {
151782743679SGarrett D'Amore 		nmp = mp->b_next;
151882743679SGarrett D'Amore 		mp->b_next = NULL;
151982743679SGarrett D'Amore 		if (ip->suspended) {
152082743679SGarrett D'Amore 			freemsg(mp);
152182743679SGarrett D'Amore 			ip->nocarrier++;
152282743679SGarrett D'Amore 			mp = nmp;
152382743679SGarrett D'Amore 			continue;
152482743679SGarrett D'Amore 		}
152582743679SGarrett D'Amore 		if ((mp = iprb_send(ip, mp)) != NULL) {
152682743679SGarrett D'Amore 			mp->b_next = nmp;
152782743679SGarrett D'Amore 			break;
152882743679SGarrett D'Amore 		}
152982743679SGarrett D'Amore 		mp = nmp;
153082743679SGarrett D'Amore 	}
153182743679SGarrett D'Amore 	mutex_exit(&ip->culock);
153282743679SGarrett D'Amore 	return (mp);
153382743679SGarrett D'Amore }
153482743679SGarrett D'Amore 
153582743679SGarrett D'Amore void
iprb_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)153682743679SGarrett D'Amore iprb_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
153782743679SGarrett D'Amore {
153882743679SGarrett D'Amore 	iprb_t	*ip = arg;
153982743679SGarrett D'Amore 
154082743679SGarrett D'Amore 	if ((ip->miih != NULL) && (mii_m_loop_ioctl(ip->miih, wq, mp)))
154182743679SGarrett D'Amore 		return;
154282743679SGarrett D'Amore 
154382743679SGarrett D'Amore 	miocnak(wq, mp, 0, EINVAL);
154482743679SGarrett D'Amore }
154582743679SGarrett D'Amore 
154682743679SGarrett D'Amore uint16_t
iprb_mii_read(void * arg,uint8_t phy,uint8_t reg)154782743679SGarrett D'Amore iprb_mii_read(void *arg, uint8_t phy, uint8_t reg)
154882743679SGarrett D'Amore {
154982743679SGarrett D'Amore 	iprb_t	*ip = arg;
155082743679SGarrett D'Amore 	uint32_t mdi;
155182743679SGarrett D'Amore 
155282743679SGarrett D'Amore 	/*
155382743679SGarrett D'Amore 	 * NB: we are guaranteed by the MII layer not to be suspended.
155482743679SGarrett D'Amore 	 * Furthermore, we have an independent MII register.
155582743679SGarrett D'Amore 	 */
155682743679SGarrett D'Amore 
155782743679SGarrett D'Amore 	mdi = MDI_OP_RD |
155882743679SGarrett D'Amore 	    ((uint32_t)phy << MDI_PHYAD_SHIFT) |
155982743679SGarrett D'Amore 	    ((uint32_t)reg << MDI_REGAD_SHIFT);
156082743679SGarrett D'Amore 
156182743679SGarrett D'Amore 	PUT32(ip, CSR_MDICTL, mdi);
156282743679SGarrett D'Amore 	for (int i = 0; i < 100; i++) {
156382743679SGarrett D'Amore 		mdi = GET32(ip, CSR_MDICTL);
156482743679SGarrett D'Amore 		if (mdi & MDI_R) {
156582743679SGarrett D'Amore 			return (mdi & 0xffff);
156682743679SGarrett D'Amore 		}
156782743679SGarrett D'Amore 		drv_usecwait(1);
156882743679SGarrett D'Amore 	}
156982743679SGarrett D'Amore 	return (0xffff);
157082743679SGarrett D'Amore }
157182743679SGarrett D'Amore 
157282743679SGarrett D'Amore void
iprb_mii_write(void * arg,uint8_t phy,uint8_t reg,uint16_t data)157382743679SGarrett D'Amore iprb_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data)
157482743679SGarrett D'Amore {
157582743679SGarrett D'Amore 	iprb_t	*ip = arg;
157682743679SGarrett D'Amore 	uint32_t mdi;
157782743679SGarrett D'Amore 
157882743679SGarrett D'Amore 	mdi = MDI_OP_WR |
157982743679SGarrett D'Amore 	    ((uint32_t)phy << MDI_PHYAD_SHIFT) |
158082743679SGarrett D'Amore 	    ((uint32_t)reg << MDI_REGAD_SHIFT) |
158182743679SGarrett D'Amore 	    (data);
158282743679SGarrett D'Amore 
158382743679SGarrett D'Amore 	PUT32(ip, CSR_MDICTL, mdi);
158482743679SGarrett D'Amore 	for (int i = 0; i < 100; i++) {
158582743679SGarrett D'Amore 		if (GET32(ip, CSR_MDICTL) & MDI_R)
158682743679SGarrett D'Amore 			break;
158782743679SGarrett D'Amore 	}
158882743679SGarrett D'Amore }
158982743679SGarrett D'Amore 
159082743679SGarrett D'Amore void
iprb_mii_notify(void * arg,link_state_t link)159182743679SGarrett D'Amore iprb_mii_notify(void *arg, link_state_t link)
159282743679SGarrett D'Amore {
159382743679SGarrett D'Amore 	iprb_t *ip = arg;
159482743679SGarrett D'Amore 
159582743679SGarrett D'Amore 	mac_link_update(ip->mach, link);
159682743679SGarrett D'Amore }
159782743679SGarrett D'Amore 
159882743679SGarrett D'Amore uint_t
iprb_intr(caddr_t arg1,caddr_t arg2)159982743679SGarrett D'Amore iprb_intr(caddr_t arg1, caddr_t arg2)
160082743679SGarrett D'Amore {
160182743679SGarrett D'Amore 	iprb_t *ip = (void *)arg1;
160282743679SGarrett D'Amore 	uint8_t	sts;
160382743679SGarrett D'Amore 	mblk_t	*mp = NULL;
160482743679SGarrett D'Amore 
160582743679SGarrett D'Amore 	_NOTE(ARGUNUSED(arg2));
160682743679SGarrett D'Amore 
160782743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
160882743679SGarrett D'Amore 	if (ip->suspended) {
160982743679SGarrett D'Amore 		mutex_exit(&ip->rulock);
161082743679SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
161182743679SGarrett D'Amore 	}
161282743679SGarrett D'Amore 	sts = GET8(ip, CSR_STS);
161382743679SGarrett D'Amore 	if (sts == 0) {
161482743679SGarrett D'Amore 		/* No interrupt status! */
161582743679SGarrett D'Amore 		mutex_exit(&ip->rulock);
161682743679SGarrett D'Amore 		return (DDI_INTR_UNCLAIMED);
161782743679SGarrett D'Amore 	}
161882743679SGarrett D'Amore 	/* acknowledge the interrupts */
161982743679SGarrett D'Amore 	PUT8(ip, CSR_STS, sts);
162082743679SGarrett D'Amore 
162182743679SGarrett D'Amore 	if (sts & (STS_RNR | STS_FR)) {
162282743679SGarrett D'Amore 		mp = iprb_rx(ip);
162382743679SGarrett D'Amore 
162482743679SGarrett D'Amore 		if ((sts & STS_RNR) &&
162582743679SGarrett D'Amore 		    ((GET8(ip, CSR_STATE) & STATE_RUS) == STATE_RUS_NORES)) {
162682743679SGarrett D'Amore 			iprb_rx_init(ip);
162782743679SGarrett D'Amore 
162882743679SGarrett D'Amore 			mutex_enter(&ip->culock);
162982743679SGarrett D'Amore 			PUT32(ip, CSR_GEN_PTR, ip->rxb[0].paddr);
163082743679SGarrett D'Amore 			/* wait for the SCB */
163182743679SGarrett D'Amore 			(void) iprb_cmd_ready(ip);
163282743679SGarrett D'Amore 			PUT8(ip, CSR_CMD, RUC_START);
163382743679SGarrett D'Amore 			(void) GET8(ip, CSR_CMD);	/* flush CSR */
163482743679SGarrett D'Amore 			mutex_exit(&ip->culock);
163582743679SGarrett D'Amore 		}
163682743679SGarrett D'Amore 	}
163782743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
163882743679SGarrett D'Amore 
163982743679SGarrett D'Amore 	if (mp) {
164082743679SGarrett D'Amore 		mac_rx(ip->mach, NULL, mp);
164182743679SGarrett D'Amore 	}
164282743679SGarrett D'Amore 	if ((sts & (STS_CNA | STS_CX)) && ip->wantw)  {
164382743679SGarrett D'Amore 		ip->wantw = B_FALSE;
164482743679SGarrett D'Amore 		mac_tx_update(ip->mach);
164582743679SGarrett D'Amore 	}
164682743679SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
164782743679SGarrett D'Amore }
164882743679SGarrett D'Amore 
164982743679SGarrett D'Amore void
iprb_periodic(void * arg)165082743679SGarrett D'Amore iprb_periodic(void *arg)
165182743679SGarrett D'Amore {
165282743679SGarrett D'Amore 	iprb_t *ip = arg;
165382743679SGarrett D'Amore 	boolean_t reset = B_FALSE;
165482743679SGarrett D'Amore 
165582743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
165682743679SGarrett D'Amore 	if (ip->suspended || !ip->running) {
165782743679SGarrett D'Amore 		mutex_exit(&ip->rulock);
165882743679SGarrett D'Amore 		return;
165982743679SGarrett D'Amore 	}
166082743679SGarrett D'Amore 
166182743679SGarrett D'Amore 	/*
166282743679SGarrett D'Amore 	 * If we haven't received a packet in a while, and if the link
166382743679SGarrett D'Amore 	 * is up, then it might be a hung chip.  This problem
166482743679SGarrett D'Amore 	 * reportedly only occurs at 10 Mbps.
166582743679SGarrett D'Amore 	 */
166682743679SGarrett D'Amore 	if (ip->rxhangbug &&
166782743679SGarrett D'Amore 	    ((ip->miih == NULL) || (mii_get_speed(ip->miih) == 10000000)) &&
16680529d5c6SJosef 'Jeff' Sipek 	    ((gethrtime() - ip->rx_wdog) > ip->rx_timeout)) {
166982743679SGarrett D'Amore 		cmn_err(CE_CONT, "?Possible RU hang, resetting.\n");
167082743679SGarrett D'Amore 		reset = B_TRUE;
167182743679SGarrett D'Amore 	}
167282743679SGarrett D'Amore 
167382743679SGarrett D'Amore 	/* update the statistics */
167482743679SGarrett D'Amore 	mutex_enter(&ip->culock);
167582743679SGarrett D'Amore 
1676*ab3f6e90SJoshua M. Clulow 	/*
1677*ab3f6e90SJoshua M. Clulow 	 * The watchdog timer is updated when we send frames or when we reclaim
1678*ab3f6e90SJoshua M. Clulow 	 * completed commands.  When the link is idle for long periods it is
1679*ab3f6e90SJoshua M. Clulow 	 * possible we will have done neither of these things, so reclaim
1680*ab3f6e90SJoshua M. Clulow 	 * explicitly before checking for a transmit stall:
1681*ab3f6e90SJoshua M. Clulow 	 */
1682*ab3f6e90SJoshua M. Clulow 	iprb_cmd_reclaim(ip);
16830529d5c6SJosef 'Jeff' Sipek 	if (ip->tx_wdog && ((gethrtime() - ip->tx_wdog) > ip->tx_timeout)) {
168482743679SGarrett D'Amore 		/* transmit/CU hang? */
168582743679SGarrett D'Amore 		cmn_err(CE_CONT, "?CU stalled, resetting.\n");
168682743679SGarrett D'Amore 		reset = B_TRUE;
168782743679SGarrett D'Amore 	}
168882743679SGarrett D'Amore 
168982743679SGarrett D'Amore 	if (reset) {
169082743679SGarrett D'Amore 		/* We want to reconfigure */
169182743679SGarrett D'Amore 		iprb_stop(ip);
169282743679SGarrett D'Amore 		if (iprb_start(ip) != DDI_SUCCESS) {
169382743679SGarrett D'Amore 			iprb_error(ip, "unable to restart chip");
169482743679SGarrett D'Amore 		}
169582743679SGarrett D'Amore 	}
169682743679SGarrett D'Amore 
169782743679SGarrett D'Amore 	iprb_update_stats(ip);
169882743679SGarrett D'Amore 
169982743679SGarrett D'Amore 	mutex_exit(&ip->culock);
170082743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
170182743679SGarrett D'Amore }
170282743679SGarrett D'Amore 
170382743679SGarrett D'Amore int
iprb_quiesce(dev_info_t * dip)170482743679SGarrett D'Amore iprb_quiesce(dev_info_t *dip)
170582743679SGarrett D'Amore {
170682743679SGarrett D'Amore 	iprb_t *ip = ddi_get_driver_private(dip);
170782743679SGarrett D'Amore 
170882743679SGarrett D'Amore 	/* Reset, but first go into idle state */
170982743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SEL_RESET);
171082743679SGarrett D'Amore 	drv_usecwait(50);
171182743679SGarrett D'Amore 	PUT32(ip, CSR_PORT, PORT_SW_RESET);
171282743679SGarrett D'Amore 	drv_usecwait(10);
171382743679SGarrett D'Amore 	PUT8(ip, CSR_INTCTL, INTCTL_MASK);
171482743679SGarrett D'Amore 
171582743679SGarrett D'Amore 	return (DDI_SUCCESS);
171682743679SGarrett D'Amore }
171782743679SGarrett D'Amore 
171882743679SGarrett D'Amore int
iprb_suspend(dev_info_t * dip)171982743679SGarrett D'Amore iprb_suspend(dev_info_t *dip)
172082743679SGarrett D'Amore {
172182743679SGarrett D'Amore 	iprb_t *ip = ddi_get_driver_private(dip);
172282743679SGarrett D'Amore 
172382743679SGarrett D'Amore 	if (ip->miih)
172482743679SGarrett D'Amore 		mii_suspend(ip->miih);
172582743679SGarrett D'Amore 
172682743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
172782743679SGarrett D'Amore 	mutex_enter(&ip->culock);
172882743679SGarrett D'Amore 	if (!ip->suspended) {
172982743679SGarrett D'Amore 		ip->suspended = B_TRUE;
173082743679SGarrett D'Amore 		if (ip->running) {
173182743679SGarrett D'Amore 			iprb_update_stats(ip);
173282743679SGarrett D'Amore 			iprb_stop(ip);
173382743679SGarrett D'Amore 		}
173482743679SGarrett D'Amore 	}
173582743679SGarrett D'Amore 	mutex_exit(&ip->culock);
173682743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
173782743679SGarrett D'Amore 	return (DDI_SUCCESS);
173882743679SGarrett D'Amore }
173982743679SGarrett D'Amore 
174082743679SGarrett D'Amore int
iprb_resume(dev_info_t * dip)174182743679SGarrett D'Amore iprb_resume(dev_info_t *dip)
174282743679SGarrett D'Amore {
174382743679SGarrett D'Amore 	iprb_t *ip = ddi_get_driver_private(dip);
174482743679SGarrett D'Amore 
174582743679SGarrett D'Amore 	mutex_enter(&ip->rulock);
174682743679SGarrett D'Amore 	mutex_enter(&ip->culock);
174782743679SGarrett D'Amore 
174882743679SGarrett D'Amore 	ip->suspended = B_FALSE;
174982743679SGarrett D'Amore 	if (ip->running) {
175082743679SGarrett D'Amore 		if (iprb_start(ip) != DDI_SUCCESS) {
175182743679SGarrett D'Amore 			iprb_error(ip, "unable to restart chip!");
175282743679SGarrett D'Amore 			ip->suspended = B_TRUE;
175382743679SGarrett D'Amore 			mutex_exit(&ip->culock);
175482743679SGarrett D'Amore 			mutex_exit(&ip->rulock);
175582743679SGarrett D'Amore 			return (DDI_FAILURE);
175682743679SGarrett D'Amore 		}
175782743679SGarrett D'Amore 	}
175882743679SGarrett D'Amore 
175982743679SGarrett D'Amore 	mutex_exit(&ip->culock);
176082743679SGarrett D'Amore 	mutex_exit(&ip->rulock);
176182743679SGarrett D'Amore 	if (ip->miih)
176282743679SGarrett D'Amore 		mii_resume(ip->miih);
176382743679SGarrett D'Amore 	return (DDI_SUCCESS);
176482743679SGarrett D'Amore }
176582743679SGarrett D'Amore 
176682743679SGarrett D'Amore int
iprb_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)176782743679SGarrett D'Amore iprb_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
176882743679SGarrett D'Amore {
176982743679SGarrett D'Amore 	switch (cmd) {
177082743679SGarrett D'Amore 	case DDI_ATTACH:
177182743679SGarrett D'Amore 		return (iprb_attach(dip));
177282743679SGarrett D'Amore 
177382743679SGarrett D'Amore 	case DDI_RESUME:
177482743679SGarrett D'Amore 		return (iprb_resume(dip));
177582743679SGarrett D'Amore 
177682743679SGarrett D'Amore 	default:
177782743679SGarrett D'Amore 		return (DDI_FAILURE);
177882743679SGarrett D'Amore 	}
177982743679SGarrett D'Amore }
178082743679SGarrett D'Amore 
178182743679SGarrett D'Amore int
iprb_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)178282743679SGarrett D'Amore iprb_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
178382743679SGarrett D'Amore {
178482743679SGarrett D'Amore 	switch (cmd) {
178582743679SGarrett D'Amore 	case DDI_DETACH:
178682743679SGarrett D'Amore 		return (iprb_detach(dip));
178782743679SGarrett D'Amore 
178882743679SGarrett D'Amore 	case DDI_SUSPEND:
178982743679SGarrett D'Amore 		return (iprb_suspend(dip));
179082743679SGarrett D'Amore 
179182743679SGarrett D'Amore 	default:
179282743679SGarrett D'Amore 		return (DDI_FAILURE);
179382743679SGarrett D'Amore 	}
179482743679SGarrett D'Amore }
179582743679SGarrett D'Amore 
179682743679SGarrett D'Amore void
iprb_error(iprb_t * ip,const char * fmt,...)179782743679SGarrett D'Amore iprb_error(iprb_t *ip, const char *fmt, ...)
179882743679SGarrett D'Amore {
179982743679SGarrett D'Amore 	va_list ap;
180082743679SGarrett D'Amore 	char buf[256];
180182743679SGarrett D'Amore 
180282743679SGarrett D'Amore 	va_start(ap, fmt);
180382743679SGarrett D'Amore 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
180482743679SGarrett D'Amore 	va_end(ap);
180582743679SGarrett D'Amore 
180682743679SGarrett D'Amore 	cmn_err(CE_WARN, "%s%d: %s",
180782743679SGarrett D'Amore 	    ddi_driver_name(ip->dip), ddi_get_instance(ip->dip), buf);
180882743679SGarrett D'Amore }
1809